diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 0000000..584d7fb --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,24 @@ +Below are instructions on how to configure, build and install Larn. I am assuming whoever decides to compile it themselves is capable of doing it without help. + +On a *NIX system (BSD, OS X etc..) + +1. Ensure cmake is installed on your platform of choice +2. 'cd' into the 'build' folder and type 'cmake -DCMAKE_BUILD_TYPE=Debug '.' (or Release) +3. Then type 'make' +4. Larn will be built in the 'build' folder, cleanly. +5. 'mv' or 'cp' the 'larn' binary to ../ so it is in the same directory as the 'data' folder. +6. './larn' to execute it and play :) + +On Windows + +You can use the CMAKE GUI on Windows or follow the *NIX instructions, replacing the commands with their Windows counterparts. + +1. in the 'win' directory under src/libs copy the 2 .h files to your TDM installation directory and put them in 'include' +2. Copy the .a library and place it in the TDM installation 'lib' directory. +3. Configure the build (source directory is 'build' and where you want it will be the same directory that the folder 'data' is in. Configure for whatever makefiles or build system you are using. +4. Click on 'Generate' and then type 'mingw32-make' +5. Double-click 'larn.exe' to play (it will be in the same directory as data anyway on Windows). + +You can also use the 'Larn' Visual Studio project (for those who use VS). If you do, you'll need the .libs in the src/libs/winvc directory and the includes (in the same directory). + +Enjoy playing Larn! \ No newline at end of file diff --git a/MANUAL.txt b/MANUAL.txt new file mode 100755 index 0000000..2eb561f --- /dev/null +++ b/MANUAL.txt @@ -0,0 +1,99 @@ + + +LARN version 14.0.0 +------------------- + +Table of contents +----------------- + +1. Introduction +2. System requirements +3. Files Supplied +4. Installation +5. Command line options +6. WIZARD mode +7. History and other Information + +1. Introduction +--------------- + +LARN is a dungeon type adventure game similar in concept to HACK, ROGUE +or MORIA, but with a different feel and winning criteria. LARN was +released for the UNIX environment in 1986 by Noah Morgan. It was +subsequently ported to the MS-DOS environment by Don Kneller. Kevin +Routley has been working on enhancements to LARN on and off for the +past two years. This version is for OSX, BSD, GNU/LINUX and WINDOWS environments. + +2. System requirements +---------------------- + +Any GNU/Linux, OSX, BSD or Windows Machine with ncurses or pdcurses. + +3. Files supplied +----------------- + +README.txt This documentation. +docs/CHANGELOG.txt Changes made since LARN V12.2 + +Binaries and 'data' folder included in this directory (same as README.txt) + +4. Installation +--------------- + +Decompress LARN into a directory of your chosing. LARN is self contained +and does not write any crap to your registry or system. + +5. Running the Game +------------------- + +Open the install directory in a command window and type "larn", or double +click .exe file inside the directory. If you are using Windows Explorer. + +If you're using *nix systems then cd into the directory and ./larn + +6. Command line options +----------------------- + +There are several command line options that can modify the behavior of +LARN. You can run these through larn.bat as well. The options are: + +-s Show scores. +-i Show all scores including the inventories of dead + players. +-# Where # is a number from 0 to 9. This sets the + difficulty of LARN to this level. Normally, LARN + starts out with difficulty 0 and increases in + difficulty by 1 when you win at the current level. + Thus the game automatically gets more difficult. +-h A help screen that shows the command line arguments. + +7. WIZARD mode +-------------- + +There is a WIZARD mode for testing features of the game. To get into +WIZARD mode, type in an underscore "_". +Wizards are non-scoring characters that get enlightenment, everlasting expanded +awareness and one of every object in the game. + +8. History and Other Information +-------------------------------- + +Noah Morgan originally created LARN 12.0 and released the UNIX +version to the USENET in 1986. Don Kneller ported the UNIX +version to MSDOS (both IBM PCs and DEC Rainbows). + +Kevin Routley contributed various LARN enhancements. Version 12.1 had +a limited distribution. Version 12.2 was distributed to the Usenet +community. Version 12.3 was the last version released by Kevin. + +Someone made 12.4 through 12.4.2, possibly copx according to +roguebasin. Edwin Denicholas took 12.4 alpha 2 and caressed it into +12.4.3 for Win32. + +Gibbon has implemented many fixes and enhancements and taken it to 14.0.0. Which is +multi-platform. + +Other editions of Larn have been distributed by others, namely +LARN13 and Ultra-Larn. + +I hope you enjoy this version of LARN. diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt new file mode 100755 index 0000000..a60bb44 --- /dev/null +++ b/build/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 2.8.6) +if(WIN32) + add_definitions(-DWINDOWS -DMULTIPLE_SCORE_ENTRY -DTERM_DARK_BACKGROUND) + else() + add_definitions(-DNIX -DMULTIPLE_SCORE_ENTRY -DTERM_DARK_BACKGROUND) +endif() +project(Larn) + +find_package(Curses REQUIRED) + +add_definitions(-DEXTRA) + +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pipe -Wall -Wextra -pedantic -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wshadow -Wmissing-declarations -Wold-style-definition -Wredundant-decls -g -std=c18") + +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -pipe -Wall -fomit-frame-pointer -std=c18") + +include_directories(../src/includes) +include_directories( ${CURSES_INCLUDE_DIRS}) +file(GLOB SOURCES "../src/*.c") +add_executable(larn ${SOURCES}) +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lm") +target_link_libraries(larn ${CURSES_LIBRARIES} ${CMAKE_DL_LIBS}) diff --git a/data/forts.txt b/data/forts.txt new file mode 100644 index 0000000..7e09ec3 --- /dev/null +++ b/data/forts.txt @@ -0,0 +1,36 @@ +gem value = gem * 2 ^ perfection +sitting down can have unexpected results +don't pry into the affairs of others +drinking can be hazardous to your health +beware of the gusher! +some monsters are greedy +nymphs have light fingers +try kissing a disenchantress! +the Eye of Larn improves with time +hammers and brains don't mix +what does a potion of cure dianthroritis taste like? +hit point gain/loss when raising a level depends on constitution +healing a mighty wizard can be exhilarating +be sure to pay your taxes +are Vampires afraid of something? +some dragons can fly +dos thou strive for perfection? +patience is a virtue, unless your daughter dies +what does the Eye of Larn see in its guardian? +a level 25 player casts like crazy! +energy rings affect spell regeneration +my, aren't you clever! +difficulty affects regeneration +control of the pesty spirits is most helpful +don't fall into a bottomless pit +dexterity allows you to carry more +you can get 2 points of WC for the price of one +never enter the dungeon naked! the monsters will laugh at you! +did someone put itching powder in your armor? +you klutz! +avoid opening doors. you never know whats on the other side. +infinite regeneration ---> temptation +the greatest weapon in the game has not the highest Weapon Class +you can't buy the most powerful scroll +identify things before you use them +there's more than one way through a wall diff --git a/data/mazefile.txt b/data/mazefile.txt new file mode 100755 index 0000000..fac6555 --- /dev/null +++ b/data/mazefile.txt @@ -0,0 +1,449 @@ +I################################################################### +# # . # # # # # . # +# D D . . D . # +###D########################################## # # ###D### +# -# #. # # ################ . .# +# ####### ######## ############ D #### # # # +# ... #.# # # # . # # # #### # ############ # ###D### +# #.# # # ## # # # ############ #### # #- # # # #. # +# . # # # # ## #- # # # - D #### # # . D # #.# # ... # +# # #.# # # # # # . . # # # # # # #-# # ~.! # +###D### ### #######D## # ############ ###### ########## ### ####### +# # @ .# # ..... ...# +###D###########################################################D### +# . #.....# # # # -# # # # # # +# ..... . D D D D. # +# #.....# # # # # # .# # # # +################################################################### + +################################################################### +#.. . D # . # #- # +############# ######### # ## ### ##### ## #### ###### ####### ### # +#.#!#~# # # .-# # #- # # # # -# # # # +# # #.# . # ####### # # # # # # # # # ##### +# # ..# ##### # # # #### # ## ## ## # ###### ####### # # +# - ..D # D # . D # # # #.##### ## ## # #. #.# #..# # ### # +####### ####### ### # # # # # # D # D D #..D # # +#- # # # #### # ###### # ## # #. # #..# ##### +### #######################- # # # ###################### # # +# ... # . #..# ### # - .. . #. ### # +# # # # ### #################### # # # +# ### # +################################################################# # +#- D ### # # # # +# . # # # D # +################################################################### + +################################################################### +# .. # +# ############## ############################################## # # +# # # # # .. # # # +# #D## # # ############D################# ########### # # # +######### #- # # # #- D # # ~ # # # # # # # +# # # # # # # ### # # # D - # # ####### # # # # +# .... # #### # # # #!# # # ###### .. #.# # # # # # # +# .... # # # # ### # # # # #########D#### # ### # # # # # +# .... ######## # # D # #- # #.. # ...#.# # #.# # # # # # +# # # ### # #### #.- D - #.# #.#.# # # # # # +#####DD## ######## # # D D. # # # #...# # # # # # +# # ..# # # # ############################## # ##### # # # # # +# ......# # # # # # # # # # +# ####### ###### ################################ ######### # ### # +# .D. # # +################################################################### + +################################################################### +# ## ## ### ## # +# ##### ## ..- ## ##.## ## # +# # ! ## ## . ## ## .## .. ## # +# #....###### ## ## ## . ## ## # +# # - # ## ##D# ## . ## ## # +# #####D ~ ####### ###........ ## ... ## ## # +# # # ## ## .... ## . ## ## # +# #. ######## ## ## . - ####D#### - # +# #.- #...## ## ... ## ### ...... ## ## # +# #. #..## ## ######### ## ... ## ### ## .. # +# #.. #.## ## ## - ## . #### ## ## ##### ## # +# #####D## ## ## ###### ## ## ####### ## # +# D -.## #### ######## ##DD## ######## #D # +# ###### ... ## ########## ## # +#### . . ### +################################################################### + +################################################################### +# # +# ####.########################################## ## ########## +# # #.#.#.# #.. #. # # # +####### # # # # # ##### #! # ########### # ### # +# # # # # # # ##...## # # # # # #-# # +# ..- D # # # # # ## . ## ####D##### .. ### # # # # +# # # # ##. ~ .## # # ### # # ##### # # +############ # # # # ## . . .## #...# .. #.# # # # # +# .. .# # # # # ## - ## # # D.D # ######### # +# . D # # # # ##.......## ####D#####.# # # +# - . # # # # # ## ## # . # ### ########### # +############ #.#.# # ###D### # . #....# # # +# # #-# D .. # .# ###....## ##-### # +#### ########################################## ###### ### # +# . # +################################################################### + +################################################################### +# # +# ###########################D####### ## # +# ####################### # ##...... ## ## # +# ########D###### # # !##.... ## ## # +# ############ ## ## ... # # #...## ~ ## ###### # +# # # # # ...... # # # .. ##### ## # +# # - .. # # # ########### # # . ######### ## # +# ##### ##### # # # # #... ## ## # +# # # ########## ######## ########DD#### ####### # +# # # .... # # # # # ## ## # +# ##### # # ....# # ######### # # ##### ## # +# #- ######D##########..# # ######### # - #.. ## ## # +# ##### # # # #... #.....## # +# # ################### ###############... #### # ###### +# # # +################################################################### + +################################################################### +# # +########### ##### ##### #####D#### ##### ###### # +#.. # #-..D ###### ## # ## # ## ..## ### #### # +# #### ### ##### # ## # ##### # ## ### ## # # # +# # # # # # ##### # ## # # #### # #..##### - ### # +# # # ####### ### #...# # ######## # # #~. .... # # +# # # # #...# # # ######### ############## # +# # ###### ####### # ### ### # .. # # +# # # - # #-#####!# # # #### ## #### # # +# ###### # ########## #.. ....# # ######## # # # ## ## # +# -.# ##### ####...## # . .# #..# # ## # # +# #### ###### #.### #####.#### #### ### ## # # +# #- #######....#### ...... #.# . # ## # # +# ####### ##### ###########.############### ### # +# . # +################################################################### + +################################################################### +# # #- . # # # # # # # # # +# # # #####.##### # # # # # # # # # # ###########D########### # +# # ##### # # ### # # # # # # # # # ### #.... # # # # +# # # # # # # # # # # # # # #.!. -.. #.##.# # # +# # . - # # ####### # # # # # # # # # ########## ## # # +#.####### # # D # . # # # +#. -# ################################################### # +# ##### D. . # +# # ### ###D### ### ### ###D### ### ### ###D### ###.### # +# # ###### ### .# .# #. # # # # # # # # . # # +# ### # -# ### .#. - .# # # #...- # # .. # # -..# # . # # +# # # ### #. ~ .# # # # # # # # .. .# # # # +#.###### # ### # # #.....# # # # # # # # # # +# # ####### ####### ####### ####### ####### ####### ####### # +# # ... .. # +################################################################### + +################################################################### +# # . # ### # # # # . # +# D D . # . D . # +############################################### # # ###D### +# -# #. # # ################ . .# +# #######D######## ############ # #### # # # +# ... #. # #!~ . # # # #--# # ############ #####D### +# #.# # # ## # # # ############ #--# # #- # # # #. # +# . # #-# # ## #- # # # - D ## # # # . D #### #.# # ... # +# # #-# # # # # # . . # # # # # # #-# # -.- # +###D### ### ####### ## # ############ ###### ########## ### ###D### +# # @ . ..... ...# +##################################### ###############D### +# . #.....# # # # -# # # # # +# ..... . D D D D. # +# #.....# # # # # .# # # # +################################################################### + +################################################################### +#.. . # D . #- # +### ######### ######### # ## ### ##### ## ########### ####### ### # +#.# # # # .-# # #- # # # # -# # # # +# # #.# . # ####### # # # # # # # # # ##### +# # ..####### # # # #### # ## ## ##!# ###### ####### # # +# - ..D # D # . D # # # #.##### ## ## # #. #.# #..# # ### # +####### ####### ### # # # # # # D # D D #..D # # +#- # # # #### # ###### # ## # #. # #..# ##### +### #######################- # # # ###################### # # +# ... # . #..# ### # - .. . #. ### # +# # # # #-# #################### # # # +# # -# # +################################-################################ # +#-..... # ####D ### # # # # +#~..... # # # # D # +################################################################### + +################################################################### +# .. # +# ############## ############################################## # # +# # # # # .. # # # +# #D## # # ############D################# ########### # # # +######### #- # # # #- D # #.!..# # # # # # # +# # # # # # # # ### ## # #....D - # # ####### # # # # +# .... # #### # # # #~# # # ###### .. #.# # # # # # # +###....## # # # ###.# # # #########D## # # ### # # # # # +# .... ######## # # D .# #- # .. # ...#.# # #.# # # # # # +# # ## # ### .# #### .- D - #.# #.#.# # # # # # +#####DD## ######## #... .# . # # # #.#. # # # # # +# # ..# # # ############################## # # ##### # # # # +# ......# # # # # # # # # # # +# ####### ###### ################################D# ######### ### # +# .D. #-.-# +################################################################### + +################################################################### +# ## ## ## ## ## # +# ############## ## ..- ## ## . ## ## # +# # # ## ## . ## ## . ##.. ## # +# #....###### ## ## ######## ####### . ## ## # +# # - # ## ## ##D# ## . ## ## # +# # D###### ###........ ## ... ## ###### # +# # # ## ## .... ## . ## ## - # # +# #. ######## ## ## . - ####D####.. D # +# #.- #...## ## ... ## ### ...... ## ##### # +# #. #..## ## ######### ## ... ## ### ## .. # +# #.. #.## ## ## - ## . #### ## ## ##### ## # +# #####D####### # ## ## ##..## ## ## ## ## ## # +# D -.## # # #### ##.-.-## ##DD## ### #### ## # +# ######. ...# #### ## #### ##### ## ##D# +####~!.... D . . # +################################################################### + +################################################################### +# # +# ####.########################################## ## ########## +# # #.#.#.# #.. . # # # +####### # # # # # ############# # ########### # ### # +# # # # # # # --##...##-- # # # # #-# # +# ..- D # # # # # #-## . ##-# ####D##### .. ### # # # # +# # # # ###. .### # ~ # ### ### ##### # # +############ # # # # ## . . .## #...# .. #.# # # +# .. .# # # # # ## - ## # # D. # ######### # +# . D # # # # ###.......### ####D#####.# # # +# - . # # # # # -## ##- # . ### ########### # +############ #.#.# # ######D###### # . ....# # # +# #!#-# # .. # . ###....## ##-### # +#### ############################## ########## ###### ### # +# D . # +################################################################### + +################################################################### +# # +# ###############D####################D# # +# ####################### # ##...... # # +# ########D###### # # ##.... # # +# ############### ## ## ... # # #...## ##### # +# # ~ # # #!...... # # # .. ##### # # +# # - .. # # # ########### # # . ######### # # +# ##### ######## # # # # # #... ## # # +# # # # ########## ######## ########D #### ###### # +# # # ....# # # # # # ## # # +# ##### # ###### # ....# # ######### # # #####D# # +# #- ## # #######..# # ######### # - #.. # +# ##### # # # #... #..... # +# # ################### ###############... #### ###### +# # # +################################################################### + +################################################################### +# # # +########### ##### # #####D#### ##### ###### # +#.. # #~..D ###### ## # ## # ## ..## ### #### # +# #### ### ##### # ## # ##### # ## ### ## # # # +# # # # # # ##### # ## # # #### # #..##### - ### # +# # # ####### ### #...# # ######## # # #!. .... # # +# # # # #...# # # ######### ############## # +# # ###### ####### # ### ### # .. # # +# # # - # #- # # # ######## #### # # +# # # ########## #.. ....# # ######## # ## ## # +# # -. ##### ####...## # . .# .. # # # +# # ###### ##### #####.#### ####### # # +# # - #######....#### ...... #.# . # ## # # +# ######### ##### ###########.############### ### # +# . # +################################################################### + +################################################################### +# D D #-..........# # # # # # +#D#D# #####.#####.# # # # # # #############D########### # +# # ##### #.#~###.# # # # # # ### .... # # # # +#D# # #.......# # # # # # # . . -.. #. #.# # # +# # . - # #.####### # # # # # ################## # # +#D####### #. # D # . # # # +#. D D -# ################################################### # +# ###### . . # +# # D ### ###D### ####### ###D### ####### ###D### ####### # +# # ###### # # .# .# #. # # # # # # # # . # # +# ### -# # # .#. - .# # # #...- # # .. # # -..# # . # # +#DD# # # # #. .# # # # # # # # .. .# # # # +#.###### # # # # #.....# # # # # # ! # # # # +# # ####### ####### ### ### ####### ### ### ####### ### ### # +# # ... .. # +################################################################### + +################################################################### +# D ## ## ## ## # # # +# - . # ####### ## -.- ## ## ## #### D # +#### # #.#-#.# .#. # .## ## # ####### . ### # # +# # - ##-#.#D ### # ..## ## ##D ### ### ### ##### # +# . # # # #.# .#. #- ##...##..###### # # # # # # +# #### ####### ### -## ##--######## #### # -.D # +# # # # ######D############# ########### # # +#####D# ########### D D # # # .##D## # +# # # # # #### # #####D############ ###- D ## #### +# ##### ####### # #!.#-# # # . D ## # # .# #~.# +# # # # # #..#####.# # ### # ## ##D###### #..# +##### # D #.# # - # # # ### ##### # # .# # ##.##..# +# . # # # # #D## # ####### . # .## .D ## # # +##### # ####### # # # ### # # ## # -# # D # +# # # ########### # ### ##### # # +################################################################### + +################################################################### +# # # # # +# # D # ######### #########################.#### # +#####.# # # ####D#### # #.#.#.# # # +# # # # # ##### # # # # # #D# # # # # # ####### +# # ##### # #...# # . # - # # ## ## # # # # # # . # +# # ## # - # D.#~#-# # # # ##...## # # # # # D..! # +# #### ### ###.. #.####D#### # ## ## # # # # # # . # +# # # # #####.# # # # #. - .# D D D D ############ +# ## # ## # # . # # ## ## # # # # #. .. # +# ######.### # ## ## # # # ## ## # # # # D . # +# ## # ## ##..##D#### # ## ## # # # # # . - # +# #. # #D## ## ## # # #. ### # #.#.# # ############ +# ### ### #### # #-. # #-# # D # +# # #. # ### # # - # ########D#################### . #### +# # # # # # # # D # +################################################################### + +################################################################### +# # - # ## ### # # +# ## # ################## # ## #### # ##### #D### # +# . # # # ##### D # # - # .. # # # +# #### # # ###D### - # -- . D D # ### # # # # +# ### # ## ## # ###### #### # # # # # # # +### #### ## ##### # ###### ### # # # # # +# # ########- ## ## # # ############################## # +# # ### ## ## ### D . # +# # ## #### ## ##!# # ###D### ####### ###D### ####### ###D### # +# # ## ## .###..##### # # #..~..# # # # # # . # # +###D#D### ##..#..## # # .- .# # . # # .. # # -..# # . # # +# # # ##...## - # # . .# # # # # # .. .# # # # +# . # .. ###### # # .# #. # # # # # # # # +# D ######### # ####### ###D### ####### ###D### ####### # +# # # D # # +################################################################### + +################################################################### +# # # # ## ## ## ## D # +# #. # -# # # # # ####### D##...## ####### ### ###### ###### # +# # # ## ## #! .##.# ## ## D.# # #~.. D # +####D###### # # .####...## ## # #######D### #######. # # .### ## +# # # ## ####D## ## # # ## .. # #### # ## +# ######## # ## ### ### ## ## #### ### # ## # # # +# ######## ###### # . ## ### # # ######D############# # +# . ## - ####### # # # +# ####### # . ## ### ## D. #####D###########D### +#####D##### # # ###. ### ##### ### - ## ### # # # # # +#. -# #.# # ####### ## # # ## # # . - . D # +#####D##### # # # ## # # ###### # # # # # # +#- .# # # #####D###### # # . # # #### ######D########### +##### ##### # # D # # D # # # # #### # +# D # D # # D # # .. # +################################################################### + +################################################################### +# D ## ## ## ## # # # +# - . # ####### ## -.- ## ## ## #### D # +#### # #.#-#.# .#. # .## ## # ####### . ### # # +# # - ##-#.#D ### # ..## ## ##D ### ### ### ##### # +# . # # # #.# .#. #- ##...##..###### # # # # # # +# #### ####### # ### -## ##--## ##### ### # -.D # +# # # # ## ######D###### ##### ####D###### # # +#####D# ########### D D # # ..##D## # +# # # # # #### # #####D############ ###- D ## #### +# ##### ####### # # .#-# # # . D ## # # .!# # # +# # # .. # # #. #####.# # ### # ## ######### . # # +##### # D #.#~# - # # # ### ##### # # .# # ##D##..# +# . # # .. # # #D## # ####### . # .## .D ## # # +##### # ####### # # # ### # # ## # -# # D # +# # # # # # ### ##### # # +################################################################### + +################################################################### +# # . # # # +# # D .!# ######### #########################.#### # +#####.# . # # ####D#### # #.#.#.# # # +# # #.. # # ##### # # # # ## #D# ## # # # # # ###D### +# # ##### # #. # # . # - # # #- ## ## -# # # # # # # . # +# # ## # - # D.# #-# # # # ##...## # # # # # #..! # +# #### ### ###. #.######### # ## ## # # # # # # . # +# # # #~#####.# # # D #. - .# # D D D ############ +# ## # .## # # . # # ## ## # # # # #. .. # +# ######.### #...## #.. # # # ## ## # # # # D . # +# ## #..## #####D#### # # ## ## # # # # # # . - # +# #. # #D## # # # #.## ### ## #.#.# # ############ +# ### ### #### # #-. # #-# # D # +# # #. # ### # # - # ############################# . #### +# # # # # # # # D # +################################################################### + +################################################################### +# # - # # # ### # # +# ## # ################## # # # #### # ##### #D### # +# . # # # ##### D # - # .. # # # +# #### # # ###D##### - # -- . D D # ### # # # # +# ### # ## ## D # ##### #### # # # # # # # +### #### ## ##### # ##### ### # # # # # +# # ########- ## ## # # ############################## # +# # ###~.## ## ### D . # +# # ##..#### ## ## # # ###D### ####### ###D### ####### ###D### # +# #. ## ## .###. ##### # # #. .# # # # # #.....# # +##D##D### ##. #. ## # # .- .# # . # # .. # # -..# #.....# # +# # # ##. ## - # # . .# # # # # # .. .# # # # +# . # .. ###### .. # # .# #. # # # # # # ! # # +# D #########D## ####### ### ### ####### ### ### ####### # +# # # D # # +################################################################### + +################################################################### +# # # # ## ## # # D # +# #. # -# # # # # ####### D##...## ####### ### ###### ###### # +# # # # # # ## ## # .. ##.# ## ## D.# # # . D # +# . # # # .#### ### # # #######D### #######. # # .### ## +####D###### # ## ####D# # # # # ##. # #### # ## +# # # ## ### # # ## #### # # # . # # # +# ##### # ###### # . ## ### # # ######D############# # +# ##### # . ## - ####### # # +# ####### # . ## ## # D. ##D################### +#####D##### # # ###. ### ### # # - ## # # # # # ...# +#. -# #.# # ####### ## # # ## # . - . D ..!# +########### # # # ## # ########### # # # # ...# +#- .# # # #####D###### # # . # # ##D################### +##### ##### # # D #..# D # # # # # # # +# D # D #.~# D # # # .. # +################################################################### + +################################################################### +# D-..-D . # +# # ##. ##### ## # ### ### ###D## . . # +# # #--# #..-# # # # # # # #....# . - . # +# # # DD # #D### # # # ### # D D!. -# . # +# # . - # .... # # # # # # # # # ###### . # +#.##D### # # # # # ## ### ### D -- D . -.# +####D#########D#######D#######D########D#####################D##### +#. D# D. . # +#### ###D###############D###############D######## # +# # .###. ### ### ### ### # # +# ## # ## ## #### .#. - . ###...- .. ### -..## +# # # # # # # # #### #. - .### ### .. .### +#.# D # # # # # ## # ###.....### ### ### ##### +# # # # # D # ############################################### +# # .. ## # # D ... D .... . ... D .......D ~ # +################################################################### diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt new file mode 100644 index 0000000..cfec631 --- /dev/null +++ b/docs/CHANGELOG.txt @@ -0,0 +1,541 @@ +Tue Dec 17 20:34:23 CET 2019 + +1. Optimised colour outputs for dark and light terminal backgrounds. +2. Fixed colour leakage on 'HP' stats. +3. Implemented Larn 12.0 style objects (removed hack-style) objects. +4. Implemented the cursor to replace the '@' for the player + Larn 12.0 had this and I don't know why we should copy Hack. + +~Gibbon + +============================================================================= + +Mon Mar 25 12:18:54 EDT 2019 + +Fixed a bug with running. The internal timer did not advance, which means +that slow monsters moved at the wrong speed (either they didn't move at all, +or they moved at normal speed) and bank interest didn't accumulate. + +--hymie! + +============================================================================= + +Thu Mar 14 10:02:08 EDT 2019 + +Took out the tax bill. It was a stupid time-wasting feature, and there is +a bug to get around it easily anyway. + +Delcaring this 14.1 + +--hymie! + +============================================================================= + +Mon Mar 11 15:36:19 EDT 2019 + +Fixed an infinite loop when creating treasure vaults after difficulty +level 2. + +I think I fixed the seg fault. + +--hymie! + +============================================================================= + +Tue Sep 18 08:34:19 EDT 2018 + +Oh no, sometimes I get a segmentation fault on level 10, and I don't know +why, and I can't make it happen "on command". + +--hymie! + +============================================================================= + +Tue Aug 28 14:02:26 EDT 2018 + +Found a bug where anytime the game asks you to pick a direction, +for example, open a door, the screen redraws. That just looks ugly. + +--hymie! + +============================================================================= + +Fri May 11 13:48:15 EDT 2018 + +Toned down the Longsword of Hymie a bit +* It gives you "Half Expanded Awareness" ... but only if the sword is + not negatively enchanted + +A scroll of magic mapping will tell you what floor you are on, if you +have teleported and don't know. + +I'm thinking about renaming the Longsword of Hymie to the Longsword of Gibbon. +:) + +--hymie! + +============================================================================= + +September 18th - October 4th 2016 + +1. Cleaned up the Windows build a little +2. Increased color usage (inventory) and picking up items +3. Nerfed OHSWORD and added AWARENESS trait to it +4. Bug fixes +5. Some functions cleaned up +6. Mazefile has 30th anniversary map added + +~Gibbon + +============================================================================= + +August - September 17th 2016 + +1. Increased color usage +2. Fixed some bugs +3. Improved some functions (math) for treasure room generation +4. Improved Windows support via Win32a pdcurses use and VC project files + +~Gibbon + +============================================================================= + +July 26th 2016 + +1. Finally changed the 'clear()' function which was conflicting with curses 'clear()'. + This is now called 'screen_clear()' to differentiate it. +2. Cleaned up some files. +3. Implemented initial color stuff (menu stats and player '@'). + +~Gibbon + +============================================================================= + +May 26 2016 + +1. Fixed a bug for web and sleep spells not appearing cleanly on a newline. + +~Gibbon + +============================================================================= + +April 27 2016 + +1. Fixed some warnings from debug builds +2. Fixed old style functions + +~Gibbon + +============================================================================= + +April 26 2016 + +1. Fixed incorrect text positions in the store when your inventory is full +2. Using -std=gnu11 for C is now a requirement (on GCC) + +~Gibbon + +============================================================================= + +April 18 2016 + +1. Hardcoded help text into help.c +2. Allowed players to escape from the 'O' command +3. Added a function for displaying 'back to game' (like retcont) +4. Changed some text around to make it suitable for other scenarios +5. Radically changed the directory structure (bringing it into the 21st century) +6. Made a new set of *nix compatible makefiles + +~Gibbon + +============================================================================= + +April 12 2016 + +1. Rewrote the readboard, writeboard and makeboard functions to be standard C. +2. Entirely removed the help code. Help is now in a .txt file to be read manually, + this isn't the 80's and we have filespace for a few extra bytes of text ;) +3. Hardcoded the welcome message into help.c +4. Cleaned up a few functions. +5. Removed macros defining functions for Windows and *NIX. Wrapped these up into ifdefs + and updated some deprecated functions to their modern counterparts. +6. General cleaning, bug fixing and improvements. + +~Gibbon + +============================================================================= + +April 5 2016 + +1. Cleaned up some debug warnings in GNU/Linux +2. Fixed some incorrectly written function definitions (old style) +3. Removed the Flail and added Hymie's sword (because he has really contributed). This is not available in the store's, it can only be found from adventuring and slaying. +4. Fixed some tgoto definitions which were defined twice. +5. Removed the need for a password for WIZARD mode. It's in the source + and it was a waste of keystrokes to keep typing it. +6. Removed the wearing of a shield by default in WIZARD mode. Cheaters shouldn't get + extra help ;) +7. Posix-ify the nap function. + +Flail was removed because I'll need to do some curses stuff to add an extra length to the +objects, otherwise curses goes nuts and it ain't pretty :) + +~Gibbon + +============================================================================= + +March 30 2016 + +1. Removed previous changes for termcap and fred fish files due to Linux somewhere +Requiring them to run. I'll remove these in the future but for GNU/Linux it'll need +more research and a dedicated fix for this platform. + +~Gibbon + +============================================================================= + +March 5th 2016 + +1. Cleaned up io.c variables and custom declarations of termcap routines. +2. De-fished io.c and tgoto to use standard termcap routines. +3. Removed tgoto.c and .h (Fred Fish files). + +~Gibbon + +============================================================================= + +February 26 2016 + +1. Replaced crusty 80's random seed macro with one that is easier to maintain (tested level generation, save and load). +2. Added stdlib.h to relevant files for 'random' function. +3. Cleaned up Bank display of text. +4. Added ability to have multiple scores in the same scorefile. + +~Gibbon + +============================================================================= + +February 20 - 22 2016 + +1. Fixed final warnings from debug and -Werror + +~Gibbon + +============================================================================= + +February 11 - 19 2016 + +1. Removed variables from larnfunc.h and added them to header files. +2. Cleaned up some internal things. + +~Gibbon + +============================================================================= + +January 1st/2nd 2016 + +1. Removed kbhit functions for Unix-like systems (inc kbhit.c .h). +2. Implemented recommended standard C fflush(NULL) in-place of lflushall. +For Windows, this is kept for now. +3. Created larn.h for keeping most of the headers in. +4. Fixed code so it can be built on BSD systems. + +~Gibbon + +============================================================================= + +December 29th 2015 + +1. Renamed OLANCE and OLANCEDEATH to OGREATSWORD and OGREATSWORDDEATH +2. Renamed Lance of Death to Great Sword of Death (a lance isn't a very fearsome weapon) +3. Indented all source files using GNU style +4. Nerfed the Great Sword of Death (-1) and bumped Ring of Protection (+1). Will make players carving monsters up just that little bit harder and balance it out better as you have strong spells and other bumps by that time. + +~Gibbon + +============================================================================= + +December 21st 2015 + +1. Merges with 12.3 and 12.4.4 codebases (rebased my fixes from 12.4alpha2) +2. Fixed monster generation +3. Fixed a bug when picking up items that would bump a duplicate entry 2 lines down. +4. General cleaning and fixing +5. Made datafiles .txt + +~Gibbon + +============================================================================= + +December 14 2015 + +1. A ton of fixes for segfaults +2. Fixed boolean bug preventing the player from falling down pits / bottomless pits + Math used is now a much more reliable random function which around (1 / 20) times you will fall down a pit and much much less + a bottomless pit. +3. Monster generation is partially fixed. More than 1 monster will be generated OR 1 type randomly generated each time the game is loaded (possibility). +4. Player can now pickup scrolls and potions without segfaults (segfaults will still happen when in WIZARD mode (investigating). But that is only for testing the game anyway. +5. Teleporting now works :) +6. Trapdoors are fixed and happen much more frequently. +7. Fixed segfault on OSX due to setmode. + +~Gibbon + +============================================================================= + +October 24 2015 + +1. OSX (Darwin) / BSD compatible defines implemented. +2. Darwin binary provided (64bit only) +3. Debug code fixed for Linux and Darwin +4. Linux Debug binary provided (Darwin in the next release) +5. Code cleanup and modernization continuing +6. Ammended the manual for the changes and new additions for the latest 12.50 version of Larn. Removed a lot of 90's cruft about floppy disks and outdated installation methods. It is also now a .odt. + +Todo: +1. Rewrite the tgoto code to a free implementation + +I want to re-license Larn, it is a bit of a pariah without any kind of license and some of the files don't allow commercial use. Rewrites for these parts of the code should be done. + +Darwin binary was built on OSX Mavericks using a freshly compiled ncurses 6. + +~Gibbon + +============================================================================= + +October 11 2015 + +Changes: + +1. setmode in io.c is only for Win32/BSD. Linux does not have (or need) this function. BSDs and Solaris systems will have this as it is a traditional UNIX function that is also in Windows. I ifdef'd this so Linux systems won't even look at it. + +2. I cleaned up and ported to modern compilers the conio.h file for linux. This is included in the code as it is required for the kbhit function. kbhit is a Windows-only function and this conio.h for Linux contains the definitions that are a direct equivalent of their Windows counterparts. This is needed for checking for carriage returns, key presses etc.. correctly. Comments are in polish but you don't need to read the code (unless you really want to). Windows will continue to use it's own and will ignore this version. + +I have included pdcurses.a and panel.a as well as the header files (pdcurses was compiled as 64bit only) so Windows users who are using 8+ (as it is usually 64bit) can use this 'as is'. Linux and UNIX users can use ncurses. + +The code still requires a large amount of cleaning up and modernizing, but for now it is playable and compiles. I tested saving, scores etc and these are working correctly. + +Enjoy :) + +~Gibbon + +============================================================================= + +Larn 12.4.4 + +Joe Neff found a bug where known spells weren't being saved correctly. Fixed. + +================================================================================ + +Larn 12.4.3 + +Win32 only. Depends on PDCurses. Name entered at character creation is now used +for scoreboard. Keypad + Shift run supported. All chars used for numerics in +codebas are now ints. Basic work converting to stdlib + curses instead of +the awkward terminal hacks. Very much a work in progress. + +================================================================================ + +Larn 12.4 + + +ANSIfication, ported to Curses, arrow key support, +message system fix, game over confirmation prompt, +removal of platform-specific cruft, removal of most options, +name/sex prompt, ... + +================================================================================ + +Larn 12.3.1 + +Prompted by Pat Ryan, fix a bug in the dropobj() code in main.c that +allowed the player to drop a negative amount of gold. In the process, fix +the backwards carriage return logic when printing the error response. +Document the new SIG_RETURNS_INT #define introduced by Bill Randle when the +software was posted. +Prompted by Lasse Oestergaard, guard against out-of-bound array references +in movem.c when on the Home level. Also fixed a bug where a 'smart' monster +would fail to move towards the player when the player was on the boundary. +Prompted by Mitch Gorman, make the EXTRA #define compile and work under +MS-DOS. + +================================================================================ + +This is a list of the fixes and enhancements made to create Larn V12.3 from +Larn 12.2. SPOILER ALERT! + +1. The player's position is now marked with an ampersand, instead of just with + the cursor. + +2. The 'G' command ("give the stairs a kick") has been removed. Since you can + tell the stairs apart (as opposed to the original Larn 12.0), this command + doesn't make sense anymore. + +3. The 'V' command has been removed and its information incorporated into the + 'v' command. + +4. An idea from Ultra-Larn: when the player enters the 5th level branch of the + bank after teleporting, the '?' in the level display is changed to a '5'. + +5. Larn -? can be used to print command line arguments. + +6. The player is no longer positioned near the shaft of the volcano when + climbing down to the first volcano level. + +7. A couple of pauses were eliminated, making some actions very fast now. + +8. The player can no longer escape punishment by donating more gold then he + possesses when praying at the altar. + +9. When performing an action and doing an inventory list, a character typed at + the "press space for more" prompt is taken as the inventory item to select. + That is, if you say 'q' for quaff, '*' to see all the potions you can quaff, + Larn used to require that you type a space before you could select a potion, + causing the list to disappear. You can now select an item in the list while + the list is displayed. You can also use Escape and Return in place of a + space. + +10. The spells/potions/scrolls inventory ('I' command) are now sorted. + +11. The '/' command has been added, to allow the user to identify objects. + You can choose to either type a character or move the cursor around to + select a character to identify (a la Hack). The only limitation is that + characters that have several objects (weapons, gems, dragons, etc) display + all the matching object names. + +12. The potion of gold detection has been changed into the potion of object + detection. It will find scrolls, books, potions, weapons, armor, and + artifacts. If you have an old savefile, all gold detection potions get + turned into object detection potions. + +13. It is now possible to find rings of cleverness in the dungeon. + +14. It is now possible for killed monsters to drop splint mail, battle axes, + cookies, and rings of cleverness. + +15. Source cleanup, reduction in the size of the executable and the memory + required, performance improvements. + +16. Fix problems with positioning the player when entering or leaving the + dungeon. You will no longer find yourself on the opposite side of the + town level when leaving the dungeon. You will no longer be able to enter + the dungeon on top of a monster. + +17. Prevented monsters from moving into the dungeon entrance, causing them to + be destroyed when the player exits the dungeon. The top dungeon level now + has the dungeon entrance character where there used to be a space. + +18. If you are standing on a chest and try and open it, you will no longer pick + it up immediately if you have auto-pickup on. + +19. Added the capability to add comments to the options file. + +20. Fixed the bug where a missing options file prevented anything from being + displayed. + +21. There is now a visible repeat count when greater than 10 (a la Hack). You + can also edit the repeat count. + +22. The 'm' command has been added to move onto an object without picking it + up (a la Hack). + +23. Fixed a problem where the a) item in the inventory couldn't be dulled. + +25. Allow a space between '-o' and the option filename. + +26. Fix possible errors when looking at the inventory. + +27. Prevent the player from changing levels into a level from the maze file with + a space that had no means of exit. + +================================================================================ + +This is a list of the fixes and enhancements made to create Larn V12.2 from +Larn 12.0. SPOILER ALERT! + +Changes made to create version 12.2 from 12.1: + +1. Add messages to improve feedback to the user. + +2. Improved screen drawing performance again. + +3. Flying monsters (bats, floating eyes) are no longer affected by traps. + +4. Added HACK-like objects, with 'original-objects' option. + +5. Added 'bold-objects' option. + +6. Fixed a bug where the game would apparently 'hang' for a long period of + time, especially just after killing a monster with a missile spell. + +7. Prevented invulnerability when doing VPR on a throne or altar. + +8. Scrolls of pulverization now have the same affect when directed against + an altar or fountain as they did directed against a throne. VPR spell + cause a waterlord to appear when used near a fountain. + +9. Added the '@' command and 'auto-pickup' option. + +10. Added 'prompt-on-objects' option. + +11. Improved monster movement performance again. + +12. You can now weild '-' to unweild your weapon. + +13. Waterlords can now be found in the dungeon, not just when washing at a + fountain. + +14. The Eye of Larn can now be sold in the Trading Post. + +15. Spells can now bounce off mirrors at an angle. + + +Changes made to create version 12.1 from 12.0: + +1. When drinking at a fountain, "improved sight" caused the "see invisible" + potion to be known by the player. The player must now identify the potion + in the usual manner. + +2. Falling through a pit told you the damage you received, but falling through + a trap door did not. Made trap doors act the same as pits. + +3. If you dropped a ring of dexterity/strength/cleverness that had been dulled + to a negative amount, the corresponding stat was permanently increased. No + longer. + +4. The potion of monster location would show invisible monsters as the floor + character on new levels. Now prevented. + +5. Selling all your gems at the bank could destroy items in your inventory. + +6. Monster creation was being allowed on closed doors. This was particularly + a problem with treasure rooms, since it meant that a monster much too + powerful for the player to handle was loose in the maze. Monsters cannot + now be created on closed doors. + +7. When entering a number (when entering gold amounts) you could not use the + backspace key to delete digits. Fixed. + +8. To make it more convenient when selling items in the Larn Trading Post, a + display of those items in the players inventory that can be sold has been + added. + +9. Performance of the display has been improved slightly. + +10. Monster movement has been improved for large numbers of monsters. It is + somewhat better on PC's, even with aggravation. + +11. I have added new mazes to LARN.MAZ. + +12. A Rogue-like command mode has been added, and is the default. The + version 12.0 prompting mode has been preserved for those who like it, + accessible via a command line option. Command letters have been added + to provide the ability to perform all the same actions as the prompt mode. + The help file and command line help have been updated. When in command + mode, the player will automatically pick up objects, and can read, quaff, + eat, look at, and pick up objects that you are standing on. + + In order to implement the new commands, the A and D commands from version + 12.0 have been changed. They are now ^A and I. For consistancy, to see + the list of known spells at the spell prompt, 'I' also shows all known + spells. diff --git a/docs/HISTORY.txt b/docs/HISTORY.txt new file mode 100644 index 0000000..c629c41 --- /dev/null +++ b/docs/HISTORY.txt @@ -0,0 +1,9 @@ +This file explains the historical nature of this Larn repository. + +This repository contains the Larn sourcecode which can trace it's ancestry back to the original 12.0 release from 1986. +If you check the CHANGELOG.txt, you will see the additions made throughout the decades. + +Since I am committed to maintaining Larn for the foreseeable future, eventually code will be rewritten and I expect +eventually in the future that nearly none of the original code would be left. + +~Gibbon diff --git a/docs/LICENSE.txt b/docs/LICENSE.txt new file mode 100644 index 0000000..cdfc49e --- /dev/null +++ b/docs/LICENSE.txt @@ -0,0 +1,78 @@ +For a long time, the 'real' license of the game 'Larn' was lost. Noah Morgan +did indeed give a terms of use which has been lost in usenet archives for 30 years. +I have found the original terms given by Noah Morgan himself on the use of the game 'Larn'. + +In short, anyone can modify, share, use, port, etc etc without restriction, as +long as no profit is made from any of those activities (including selling binaries). +Distribution is to be free with zero profit pertaining to any portion of Larn +including it's sources. + +This makes Larn incompatible with 'Open Source' licenses. This does not affect +variants rewritten individually, just to the historic, original version +(and subsequent versions) of the game. + +I myself ATSB (Gibbon) will follow the same terms given in Noah's original license. +All of my code, changes, modifications etc during my maintenance of the game +'Larn' are under Noah's license. + +The below is the original posting pertaining to the future of Larn, by Noah Morgan, +original author and copyright holder: + +Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP +Path: utzoo!watmath!clyde!caip!topaz!husc6!harvard!panda!condor!noah +From: noah@condor.UUCP (Noah Morgan) +Newsgroups: net.sources.games,net.games +Subject: larn and coming attractions +Message-ID: <1288@condor.UUCP> +Date: Thu, 10-Jul-86 10:56:04 EDT +Article-I.D.: condor.1288 +Posted: Thu Jul 10 10:56:04 1986 +Date-Received: Sat, 12-Jul-86 03:09:14 EDT +Distribution: net +Organization: GenRad, Inc., Bolton, Mass. +Lines: 43 +Xref: watmath net.sources.games:685 net.games:2998 + +-----------[ Eat this, you Demon lord! ]------------------------------------- + +OK folks. Its time to clarify the future of larn. There have been sufficient +comments and inquiries about it that I feel I should say whats in the works. +(It might also avoid some people trying to do what I am doing, ie duplication +of effort). Version 12.0 of larn will be released in about 2 weeks. It will +use termcap for those of you that don't have any VT100 type terminals (#1 +complaint). Thanks here goes to Michiel Huisjes for a clean termcap patch. +Larn 12.0 will be able to use playerid's instead of userid's for scoreboard +management. This allows those sites with several people playing from one uid to +have individual scoreboard entries based on their unique playerid #. Much of +the code has been rewritten to 1. make it faster and more bullet proof, 2. make +it more portable. An attempt was made to fix the problems with unsigned chars +only on machines, as well as those 16 bit ints. I now use shorts and longs, +respectively. Intelligent monster movement has been added for those monsters +smart enough to use it. (This changes the nature of the game a little). +A .holiday file has been added. Many bugs have been fixed, and several new +items have been added to the game, and overall, a general rewrite. +Larn has been run through lint, and the real problems it reports are being +tended to. If you have any suggestions for improvement of larn, feel free +to email me your suggestions. I will consider them. Please leave your +rudeness and name calling in YOUR mbox, not mine. + +I'd also like to clarify my position on the source code to larn: + +Larn was distributed to the net for the enjoyment of all. One of my goals +is to have larn available to whom ever wants to play it. I therefore give +permission to use the sources, to modify the sources, or to port the sources to +another machine, provided that a profit is not made from larn or its sources, +or the aforementioned activities. Should a profit be made without permissions, +I will exercise my copyright. Other than this case, enjoy it! + +One more thing: I will not be porting larn to PC's or Amiga's. Other +poeple on the net have expressed an intention to do this, and I will leave it +in their hands. I also leave it to those poeple to notify the net if they +develop such a version. + + + ___ Prince of Gems (alias Noah Morgan) + /. \ panda!condor!noah + \ / at GenRad Inc. Bolton MA + \ / + v diff --git a/src/action.c b/src/action.c new file mode 100644 index 0000000..a14db01 --- /dev/null +++ b/src/action.c @@ -0,0 +1,658 @@ +/* +Larn was distributed to the net for the enjoyment of all. One of my goals +is to have larn available to whom ever wants to play it. I therefore give +permission to use the sources, to modify the sources, or to port the sources to +another machine, provided that a profit is not made from larn or its sources, +or the aforementioned activities. Should a profit be made without permissions, +I will exercise my copyright. + +Other than this case, enjoy it! + +For more information, see LICENSE.txt in the 'doc' folder. +*/ + +/* +action.c + +Routines to perform the actual actions associated with various +player entered commands. + +act_remove_gems remove gems from a throne +act_sit_throne sit on a throne +act_up_stairs go up stairs +act_down_stairs go down stairs +act_drink_fountain drink from a fountain +act_wash_fountain wash at a fountain +act_up_shaft up volcanic shaft +act_down_shaft down volcanic shaft +volshaft_climbed place player near volcanic shaft +act_desecrate_altar desecrate an altar +act_donation_pray pray, donating money +act_just_pray pray, not donating money +act_prayer_heard prayer was heard +act_ignore_altar ignore an altar +act_open_chest open a chest +act_open_door open a door +*/ + +#include +#include +#include "includes/action.h" +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/create.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/io.h" +#include "includes/main.h" +#include "includes/monster.h" +#include "includes/moreobj.h" +#include "includes/object.h" +#include "includes/scores.h" +#include "includes/sysdep.h" + +static void volshaft_climbed (int); +static void act_prayer_heard (void); + +/* +act_remove_gems + +Remove gems from a throne. + +arg is zero if there is a gnome king associated with the throne + +Assumes that cursors() has been called previously, and that a check +has been made that the throne actually has gems. +*/ +void +act_remove_gems (int arg) +{ + int i, k; + + k = rnd (101); + + if (k < 25) + { + + for (i = 0; i < rnd (4); i++) + { + + /* gems pop ohf the throne */ + creategem (); + } + + item[playerx][playery] = ODEADTHRONE; + know[playerx][playery] = 0; + + } + else if (k < 40 && arg == 0) + { + + createmonster (GNOMEKING); + item[playerx][playery] = OTHRONE2; + know[playerx][playery] = 0; + + } + else + { + + lprcat ("\nNothing happens"); + } +} + + + +/* +act_sit_throne + +Sit on a throne. + +arg is zero if there is a gnome king associated with the throne + +Assumes that cursors() has been called previously. +*/ +void +act_sit_throne (int arg) +{ + int k; + + k = rnd (101); + + if (k < 30 && arg == 0) + { + + createmonster (GNOMEKING); + item[playerx][playery] = OTHRONE2; + know[playerx][playery] = 0; + + } + else if (k < 35) + { + + lprcat ("\nZaaaappp! You've been teleported!\n"); + oteleport (0); + + } + else + { + + lprcat ("\nNothing happens"); + } +} + + + +/* +assumes that cursors() has been called and that a check has been made that +the user is actually standing at a set of up stairs. +*/ +void +act_up_stairs (void) +{ + + if (level >= 2 && level != 11) + { + + newcavelevel (level - 1); + draws (0, MAXX, 0, MAXY); + bot_linex (); + refresh(); + + } + else + { + + lprcat ("\nThe stairs lead to a dead end!"); + } +} + + + +/* +assumes that cursors() has been called and that a check has been made that +the user is actually standing at a set of down stairs. +*/ +void +act_down_stairs (void) +{ + + if (level != 0 && level != 10 && level != 13) + { + + newcavelevel (level + 1); + draws (0, MAXX, 0, MAXY); + bot_linex (); + refresh(); + + } + else + { + + lprcat ("\nThe stairs lead to a dead end!"); + } +} + + + +/* +Code to perform the action of drinking at a fountian. Assumes that +cursors() has already been called, and that a check has been made that +the player is actually standing at a live fountain. +*/ +void +act_drink_fountain (void) +{ + int x; + + if (rnd (1501) < 2) + { + lprcat ("\nOops! You seem to have caught the dreadful sleep!"); + lflush (); + nap (NAPTIME); + died (280); + return; + } + + x = rnd (100); + if (x < 7) + { + cdesc[HALFDAM] += 200 + rnd (200); + lprcat ("\nYou feel a sickness coming on"); + } + + else if (x < 13) + quaffpotion (23, FALSE); /* see invisible,but don't know the potion */ + + else if (x < 45) + lprcat ("\nnothing seems to have happened"); + + else if (rnd (3) != 2) + fntchange (1); /* change char levels upward */ + + else + fntchange (-1); /* change char levels downward */ + + if (rnd (12) < 3) + { + lprcat ("\nThe fountains bubbling slowly quiets"); + item[playerx][playery] = ODEADFOUNTAIN; /* dead fountain */ + know[playerx][playery] = 0; + } + return; +} + + + +/* +Code to perform the action of washing at a fountain. Assumes that +cursors() has already been called and that a check has been made that +the player is actually standing at a live fountain. +*/ +void +act_wash_fountain (void) +{ + int x; + + if (rnd (100) < 11) + { + x = rnd ((level << 2) + 2); + lprintf ("\nOh no! The water was foul! You suffer %d hit points!", + (int) x); + lastnum = 273; + losehp (x); + bottomline (); + cursors (); + } + + else if (rnd (100) < 29) + lprcat ("\nYou got the dirt off!"); + + else if (rnd (100) < 31) + lprcat + ("\nThis water seems to be hard water! The dirt didn't come off!"); + + else if (rnd (100) < 34) + createmonster (WATERLORD); /* make water lord */ + + else + lprcat ("\nnothing seems to have happened"); + + return; +} + + + +/* +Perform the act of climbing down the volcanic shaft. Assumes +cursors() has been called and that a check has been made that +are actually at a down shaft. +*/ +void +act_down_shaft (void) +{ + if (level != 0) + { + lprcat ("\nThe shaft only extends 5 feet downward!"); + return; + } + + if (packweight () > 45 + 3 * (cdesc[STRENGTH] + cdesc[STREXTRA])) + { + lprcat ("\nYou slip and fall down the shaft"); + lastnum = 275; + losehp (30 + rnd (20)); + bottomhp (); + } + + newcavelevel (MAXLEVEL); + draws (0, MAXX, 0, MAXY); + bot_linex (); + return; +} + + + +/* +Perform the action of climbing up the volcanic shaft. Assumes +cursors() has been called and that a check has been made that +are actually at an up shaft. + +*/ +void +act_up_shaft (void) +{ + if (level != 11) + { + lprcat + ("\nThe shaft only extends 8 feet upwards before you find a blockage!"); + return; + } + + if (packweight () > 45 + 5 * (cdesc[STRENGTH] + cdesc[STREXTRA])) + { + lprcat ("\nYou slip and fall down the shaft"); + lastnum = 275; + losehp (15 + rnd (20)); + bottomhp (); + return; + } + + lflush (); + newcavelevel (0); + volshaft_climbed (OVOLDOWN); + return; +} + + + +/* +Perform the action of placing the player near the volcanic shaft +after it has been climbed. + +Takes one parameter: the volcanic shaft object to be found. If have +climbed up, search for OVOLDOWN, otherwise search for OVOLUP. +*/ +static void +volshaft_climbed (int object) +{ + int i, j; + + /* place player near the volcanic shaft */ + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + if (item[j][i] == object) + { + playerx = j; + playery = i; + positionplayer (); + i = MAXY; + break; + } + draws (0, MAXX, 0, MAXY); + bot_linex (); + return; +} + + + +/* +Perform the actions associated with Altar desecration. +*/ +void +act_desecrate_altar (void) +{ + if (rnd (100) < 60) + { + createmonster (makemonst (level + 2) + 8); + cdesc[AGGRAVATE] += 2500; + } + else if (rnd (101) < 30) + { + lprcat ("\nThe altar crumbles into a pile of dust before your eyes"); + forget (); /* remember to destroy the altar */ + } + else + lprcat ("\nnothing happens"); + return; +} + + + +/* +Perform the actions associated with praying at an altar and giving a +donation. +*/ +void +act_donation_pray (void) +{ + int k, temp; + + for (;;) + { + lprcat ("\n\n"); + cursor (1, 24); + cltoeoln (); + cursor (1, 23); + cltoeoln (); + lprcat ("how much do you donate? "); + k = readnum ((int) cdesc[GOLD]); + + lprcat ("\n"); + + /* make giving zero gold equivalent to 'just pray'ing. Allows player to + 'just pray' in command mode, without having to add yet another command. + */ + if (k == 0) + { + act_just_pray (); + return; + } + + if (cdesc[GOLD] >= k) + { + temp = cdesc[GOLD] / 10; + cdesc[GOLD] -= k; + bottomline (); + + /* if player gave less than 10% of _original_ gold, make a monster + */ + if (k < temp || k < rnd (50)) + { + /* added by ~Gibbon */ + lprcat ("You have offended the Gods."); + createmonster (makemonst (level + 1)); + cdesc[AGGRAVATE] += 200; + return; + } + if (rnd (101) > 50) + { + act_prayer_heard (); + return; + } + if (rnd (43) == 5) + { + if (cdesc[WEAR]) + lprcat ("You feel your armor vibrate for a moment"); + enchantarmor (); + return; + } + if (rnd (43) == 8) + { + if (cdesc[WIELD]) + lprcat ("You feel your weapon vibrate for a moment"); + enchweapon (); + return; + } + + lprcat ("Thank You."); + return; + } + + /* Player donates more gold than they have. Loop back around so + player can't escape the altar for free. + */ + lprcat ("You don't have that much!"); + } +} + + + +/* +Performs the actions associated with 'just praying' at the altar. Called +when the user responds 'just pray' when in prompt mode, or enters 0 to +the money prompt when praying. + +Assumes cursors(), and that any leading \n have been printed +*/ +void +act_just_pray (void) +{ + if (rnd (100) < 75) + lprcat ("nothing happens"); + else if (rnd (43) == 10) + { + if (cdesc[WEAR]) + lprcat ("You feel your armor vibrate for a moment"); + enchantarmor (); + return; + } + else if (rnd (43) == 10) + { + if (cdesc[WIELD]) + lprcat ("You feel your weapon vibrate for a moment"); + enchweapon (); + return; + } + else + createmonster (makemonst (level + 1)); + return; +} + + + +/* +* function to cast a +3 protection on the player +*/ +static void +act_prayer_heard (void) +{ + + lprcat ("You have been heard!"); + + if (cdesc[ALTPRO] == 0) + { + + cdesc[MOREDEFENSES] += 3; + } + + /* protection field */ + cdesc[ALTPRO] += 500; + + bottomline (); +} + + + +/* +Performs the act of ignoring an altar. + +Assumptions: cursors() has been called. +*/ +void +act_ignore_altar (void) +{ + if (rnd (100) < 30) + { + createmonster (makemonst (level + 1)); + cdesc[AGGRAVATE] += rnd (450); + } + else + lprcat ("\nNothing happens"); + return; +} + + + +/* +Performs the act of opening a chest. + +Parameters: x,y location of the chest to open. +Assumptions: cursors() has been called previously +*/ +void +act_open_chest (int x, int y) +{ + int i, k; + + k = rnd (101); + if (k < 40) + { + lprcat ("\nThe chest explodes as you open it"); + i = rnd (10); + lastnum = 281; /* in case he dies */ + lprintf ("\nYou suffer %d hit points damage!", (int) i); + checkloss (i); + switch (rnd (10)) /* see if he gets a curse */ + { + case 1: + cdesc[ITCHING] += rnd (1000) + 100; + lprcat ("\nYou feel an irritation spread over your skin!"); + break; + + case 2: + cdesc[CLUMSINESS] += rnd (1600) + 200; + lprcat ("\nYou begin to lose hand to eye coordination!"); + break; + + case 3: + cdesc[HALFDAM] += rnd (1600) + 200; + lprcat ("\nA sickness engulfs you!"); + break; + }; + item[x][y] = know[x][y] = 0; /* destroy the chest */ + if (rnd (100) < 69) + creategem (); /* gems from the chest */ + dropgold (rnd (110 * iarg[playerx][playery] + 200)); + for (i = 0; i < rnd (4); i++) + something (iarg[playerx][playery] + 2); + } + else + lprcat ("\nNothing happens"); + return; +} + + + +/* +Perform the actions common to command and prompt mode when opening a +door. Assumes cursors(). + +Parameters: the X,Y location of the door to open. +Return value: TRUE if successful in opening the door, false if not. +*/ +int +act_open_door (int x, int y) +{ + if (rnd (11) < 7) + { + switch (iarg[x][y]) + { + case 6: + lprcat ("\nThe door makes an awful groan, but remains stuck"); + cdesc[AGGRAVATE] += rnd (400); + break; + + case 7: + lprcat ("\nYou are jolted by an electric shock"); + lastnum = 274; + losehp (rnd (20)); + bottomline (); + break; + + case 8: + lprcat ("\nYou feel drained"); + loselevel (); + break; + + case 9: + lprcat ("\nYou suddenly feel weaker"); + if (cdesc[STRENGTH] > 3) + cdesc[STRENGTH]--; + bottomline (); + break; + + default: + lprcat ("\nThe door makes an awful groan, but remains stuck"); + break; + } + return (0); + } + else + { + lprcat ("\nThe door opens"); + know[x][y] = 0; + item[x][y] = OOPENDOOR; + return (1); + } +} diff --git a/src/ansiterm.c b/src/ansiterm.c new file mode 100644 index 0000000..d74a841 --- /dev/null +++ b/src/ansiterm.c @@ -0,0 +1,396 @@ +/* +* this is hackjob that translates the ANSI escape sequences used by larn +* to Curses API calls +*/ + +#include +#include +#include +#include +#include "includes/ansiterm.h" + +#define ANSITERM_ESC 27 + +static void ansiterm_command (int, const char *, const char *); +static void ansiterm_putchar (int); +static void llrefresh (void); + +/* +* writes to the terminal +*/ +void +ansiterm_out (const char *output_buffer, int n_chars) +{ + int i; + + i = 0; + + while (i < n_chars) + { + + if (output_buffer[i] == ANSITERM_ESC) + { + char ansi_param[10]; + char param1[5]; + char param2[5]; + int ansi_cmd; + int j; + + *param1 = '\0'; + *param2 = '\0'; + + ++i; + ++i; + + j = 0; + + while (!isalpha (output_buffer[i])) + { + + ansi_param[j] = output_buffer[i]; + + ++i; + ++j; + } + + ansi_param[j] = '\0'; + + ansi_cmd = output_buffer[i]; + ++i; + + if (*ansi_param != '\0') + { + char *p; + int k; + + p = ansi_param; + + k = 0; + while (*p != ';' && *p != '\0') + { + + param1[k] = *p; + ++p; + ++k; + } + param1[k] = '\0'; + + if (*p == ';') + ++p; + + k = 0; + while (*p != ';' && *p != '\0') + { + + param2[k] = *p; + ++p; + ++k; + } + + param2[k] = '\0'; + + } + + ansiterm_command (ansi_cmd, param1, param2); + + } + else + { + + ansiterm_putchar (output_buffer[i]); + ++i; + } + } + + /* + ESC[;H + ESC[y;xH ESC[24;01H + ESC[2J + ESC[1m standout on + ESC[m standout off + */ + llrefresh (); +} + +/******************************************** +* CURSES BACK-END * +********************************************/ + +static int llgetch (void); + +void +ansiterm_init (void) +{ + initscr(); + cbreak(); + noecho(); + nonl(); + intrflush(stdscr, FALSE); + start_color(); + keypad(stdscr, TRUE); + +/* this is so the terminal does not display in nasty grey'ish color ~Gibbon */ + use_default_colors(); + +#if defined TERM_DARK_BACKGROUND +/*Colors for a black terminal background*/ + init_pair(1, COLOR_CYAN, -1); /*stat bar text*/ + init_pair(2, COLOR_RED, -1); /*monster text and health drops*/ + init_pair(3, COLOR_YELLOW, -1); /*store texts like gold pieces */ + init_pair(4, COLOR_CYAN, -1); /*inventory text*/ +#else +/*Colors for a white/light terminal background*/ + init_pair(1, COLOR_MAGENTA, -1); + init_pair(2, COLOR_RED, -1); + init_pair(3, COLOR_BLUE, -1); + init_pair(4, COLOR_MAGENTA, -1); +#endif +/*Cursor is shown instead of '@' as that is what Larn 12.0 had. + * + *~Gibbon +*/ + /*curs_set(0);*/ + refresh(); + +#if defined WINDOWS + PDC_save_key_modifiers (1); +#endif +} + +void +ansiterm_clean_up (void) +{ + nocbreak (); + nl (); + echo (); + endwin (); +} + +/* +* get char +*/ +int +ansiterm_getch (void) +{ + return llgetch (); +} + +/* +* get char (with echo) +*/ +int +ansiterm_getche (void) +{ + int key; + echo (); + key = llgetch(); + noecho (); + return key; +} +/* wgetch() is the modern way. -Gibbon */ +static int +llgetch (void) +{ + int key; + key = wgetch(stdscr); + +#ifdef WINDOWS + if (PDC_get_key_modifiers () & PDC_KEY_MODIFIER_SHIFT) + { + switch (key) + { + case '1': + return 'B'; + case '2': + return 'J'; + case '3': + return 'N'; + case '4': + return 'H'; + case '5': + return '.'; + case '6': + return 'L'; + case '7': + return 'Y'; + case '8': + return 'K'; + case '9': + return 'U'; + } + } +#endif + switch (key) + { + case KEY_UP: + return 'k'; + case KEY_DOWN: + return 'j'; + case KEY_LEFT: + return 'h'; + case KEY_RIGHT: + return 'l'; +#ifdef WINDOWS + case KEY_A2: + return 'k'; + case KEY_B1: + return 'h'; + case KEY_B3: + return 'l'; + case KEY_C2: + return 'j'; + case PADENTER: + return 13; +#endif + case KEY_A1: + return 'y'; + case KEY_A3: + return 'u'; + case KEY_C1: + return 'b'; + case KEY_C3: + return 'n'; + case KEY_B2: + return '.'; + case KEY_ENTER: + return 13; + default: + return key; + } +} + +void +ansiterm_delch (void) +{ + delch (); +} + +static void +ansiterm_command (int ansi_cmd, const char *param1, const char *param2) +{ + if (ansi_cmd == 'H') + { + int y, x; + + if (*param1 == '\0') + { + + y = 0; + + } + else + { + + y = atoi (param1) - 1; + } + + if (*param2 == '\0') + { + + x = 0; + + } + else + { + + x = atoi (param2) - 1; + } + + move (y, x); + + } + else if (ansi_cmd == 'J') + { + + clear(); + + } + else if (ansi_cmd == 'M') + { + int i, n_lines; + + if (*param1 != '\0') + { + + n_lines = 1; + + } + else + { + + n_lines = atoi (param1); + } + + for (i = 0; i < n_lines; i++) + { + + move (0, 0); + clrtoeol (); + } + + } + else if (ansi_cmd == 'K') + { + + clrtoeol (); + + } + else if (ansi_cmd == 'm') + { + int attribute; + + if (*param1 == '\0') + { + + attribute = 0; + + } + else + { + + attribute = atoi (param1); + } + + if (attribute == 0) + { + + attrset (A_NORMAL); + + } + else if (attribute == 1) + { + + attrset (A_REVERSE); + } + + } + else + { + + exit (EXIT_FAILURE); + } +} + +static void +ansiterm_putchar (int c) +{ + if (c == '\n') + { + int y, x; + getyx(stdscr, y, x); + move(y + 1, 0); + return; + } + if (c == '\t') + { + addstr (" "); + return; + } + addch(c); +} + +void +llrefresh (void) +{ + refresh(); +} diff --git a/src/bill.c b/src/bill.c new file mode 100644 index 0000000..42c2fec --- /dev/null +++ b/src/bill.c @@ -0,0 +1,201 @@ +/* bill.c */ +#include "includes/bill.h" +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/help.h" +#include "includes/io.h" + +static int letter1 (int); +static int letter2 (void); +static int letter3 (void); +static int letter4 (void); +static int letter5 (void); +static int letter6 (void); + +/* +* function to create the tax bill for the user +*/ +static int +letter1 (int gold) +{ + resetscroll (); + screen_clear(); + + lstandout ("From:"); + lprcat (" the LRS (Larn Revenue Service)\n"); + lstandout ("\nSubject:"); + lprcat (" undeclared income\n"); + + lprcat ("\n We heard you survived the caverns of Larn. Let me be the"); + lprcat + ("\nfirst to congratulate you on your success. It is quite a feat."); + lprcat ("\nIt must also have been very profitable for you."); + lprcat ("\n\n The Dungeon Master has informed us that you brought"); + + lprintf ("\n%d gold pieces back with you from your journey. As the", gold); + + lprcat + ("\ncounty of Larn is in dire need of funds, we have spared no time"); + lprintf ("\nin preparing your tax bill. You owe %d gold pieces as", + gold * TAXRATE); + + lprcat ("\nof this notice, and is due within 5 days. Failure to pay will"); + lprcat ("\nmean penalties. Once again, congratulations, We look forward"); + lprcat ("\nto your future successful expeditions.\n"); + + retcont (); + + return (1); +} + + + +static int +letter2 (void) +{ + resetscroll (); + screen_clear(); + + lstandout ("From:"); + lprcat (" His Majesty King Wilfred of Larndom\n"); + lstandout ("\nSubject:"); + lprcat (" a noble deed\n"); + + lprcat ("\n I have heard of your magnificent feat, and I, King Wilfred,"); + lprcat + ("\nforthwith declare today to be a national holiday. Furthermore,"); + lprcat ("\nhence three days, Ye be invited to the castle to receive the"); + lprcat + ("\nhonour of Knight of the realm. Upon thy name shall it be written. . ."); + lprcat ("\nBravery and courage be yours."); + lprcat ("\nMay you live in happiness forevermore . . .\n"); + + retcont (); + + return (1); +} + + + +static int +letter3 (void) +{ + resetscroll (); + screen_clear(); + + lstandout ("From:"); + lprcat (" Count Endelford\n"); + lstandout ("\nSubject:"); + lprcat (" You Bastard!\n"); + + lprcat ("\n I heard (from sources) of your journey. Congratulations!"); + lprcat ("\nYou Bastard! With several attempts I have yet to endure the"); + lprcat (" caves,\nand you, a nobody, makes the journey! From this time"); + lprcat (" onward, bewarned\nupon our meeting you shall pay the price!\n"); + + retcont (); + + return (1); +} + + + +static int +letter4 (void) +{ + resetscroll (); + screen_clear(); + + lstandout ("From:"); + lprcat (" Mainair, Duke of Larnty\n"); + lstandout ("\nSubject:"); + lprcat (" High Praise\n"); + + lprcat + ("\n With a certainty a hero I declare to be amongst us! A nod of"); + lprcat ("\nfavour I send to thee. Me thinks Count Endelford this day of"); + lprcat ("\nright breath'eth fire as of dragon of whom ye are slayer. I"); + lprcat ("\nyearn to behold his anger and jealously. Should ye choose to"); + lprcat ("\nunleash some of thy wealth upon those who be unfortunate, I,"); + lprcat ("\nDuke Mainair, Shall equal thy gift also.\n"); + + retcont (); + + return (1); +} + + + +static int +letter5 (void) +{ + resetscroll (); + screen_clear(); + + lstandout ("From:"); + lprcat (" St. Mary's Children's Home\n"); + lstandout ("\nSubject:"); + lprcat (" these poor children\n"); + + lprcat ("\n News of your great conquests has spread to all of Larndom."); + lprcat ("\nMight I have a moment of a great man's time. We here at St."); + lprcat ("\nMary's Children's Home are very poor, and many children are"); + lprcat ("\nstarving. Disease is widespread and very often fatal without"); + lprcat + ("\ngood food. Could you possibly find it in your heart to help us"); + lprcat ("\nin our plight? Whatever you could give will help much."); + lprcat ("\n(your gift is tax deductible)\n"); + + retcont (); + + return (1); +} + + + +static int +letter6 (void) +{ + resetscroll (); + screen_clear(); + + lstandout ("From:"); + lprcat (" The National Cancer Society of Larn\n"); + lstandout ("\nSubject:"); + lprcat (" hope\n"); + + lprcat + ("\nCongratulations on your successful expedition. We are sure much"); + lprcat + ("\ncourage and determination were needed on your quest. There are"); + lprcat ("\nmany though, that could never hope to undertake such a journey"); + lprcat ("\ndue to an enfeebling disease -- cancer. We at the National"); + lprcat ("\nCancer Society of Larn wish to appeal to your philanthropy in"); + lprcat ("\norder to save many good people -- possibly even yourself a few"); + lprcat + ("\nyears from now. Much work needs to be done in researching this"); + lprcat + ("\ndreaded disease, and you can help today. Could you please see it"); + lprcat ("\nin your heart to give generously? Your continued good health"); + lprcat ("\ncan be your everlasting reward.\n"); + + retcont (); + + return (1); +} + + +/* +* Page the mail to the terminal - dgk +*/ +void +readmail (int gold) +{ + letter1 (gold); + letter2 (); + letter3 (); + letter4 (); + letter5 (); + letter6 (); +} diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..74b05a7 --- /dev/null +++ b/src/config.c @@ -0,0 +1,31 @@ +/* + * config.c -- This defines the installation dependent variables. + * Some strings are modified later. ANSI C would + * allow compile time string concatenation, we must + * do runtime concatenation, in main. + */ +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" + +#ifndef WIZID +#define WIZID 0 +#endif + +/* + * All these strings will be appended to in main() to be complete filenames + */ + + +/* Make LARNHOME readable from the larnopt file into a lardir variable. + */ +char savefilename[PATHLEN]; +char scorefile[PATHLEN]; +char logfile[PATHLEN]; +char mazefile[PATHLEN]; +char fortfile[PATHLEN]; +char playerids[PATHLEN]; + +#ifdef EXTRA +char diagfile[PATHLEN]; /* the diagnostic filename */ +#endif diff --git a/src/create.c b/src/create.c new file mode 100644 index 0000000..e7359c9 --- /dev/null +++ b/src/create.c @@ -0,0 +1,780 @@ +/* create.c */ +#include "includes/create.h" +#include "includes/larn.h" +#include "includes/global.h" +#include "includes/io.h" +#include "includes/monster.h" +#include "includes/savelev.h" +#include "includes/scores.h" + +static void makemaze (int); +static int cannedlevel (int); +static void treasureroom (int); +static void troom (int, int, int, int, int, int); +static void makeobject (int); +static void fillmroom (int, int, int); +static void froom (int, int, int); +static void fillroom (int, int); +static void sethp (int); +static void checkgen (void); + + + +/* +makeplayer() + +subroutine to create the player and the players attributes +this is called at the beginning of a game and at no other time +*/ +void +makeplayer (void) +{ + int i; + + scbr (); + screen_clear(); + + /* start player off with 15 hit points */ + cdesc[HPMAX] = cdesc[HP] = 10; + + /* player starts at level one */ + cdesc[LEVEL] = 1; + + /* total # spells starts off as 3 */ + cdesc[SPELLMAX] = cdesc[SPELLS] = 1; + + /* start regeneration correctly */ + cdesc[REGENCOUNTER] = 16; + cdesc[ECOUNTER] = 96; + + cdesc[SHIELD] = cdesc[WEAR] = cdesc[WIELD] = -1; + + for (i = 0; i < 26; i++) + { + + iven[i] = 0; + } + + /* he knows protection, magic missile */ + spelknow[0] = spelknow[1] = 1; + + if (cdesc[HARDGAME] <= 0) + { + + iven[0] = OLEATHER; + iven[1] = ODAGGER; + iven[2] = 0; + ivenarg[1] = ivenarg[0] = cdesc[WEAR] = 0; + cdesc[WIELD] = 1; + } + + playerx = rnd (MAXX - 2); + playery = rnd (MAXY - 2); + + regen_bottom = TRUE; + + /* make the attributes, ie str, int, etc. */ + for (i = 0; i < 6; i++) + { + + cdesc[i] = 12; + } + + recalc (); + + screen_clear(); + + enter_name(); +} + + +/* +newcavelevel(level) +int level; + +function to enter a new level. This routine must be called anytime the +player changes levels. If that level is unknown it will be created. +A new set of monsters will be created for a new level, and existing +levels will get a few more monsters. +Note that it is here we remove genocided monsters from the present level. +*/ +void +newcavelevel (int x) +{ + int i, j; + + if (beenhere[level]) + savelevel (); /* put the level back into storage */ + level = x; /* get the new level and put in working storage */ + if (beenhere[x]) + { + getlevel (); + sethp (0); + positionplayer (); + checkgen (); + return; + } + + /* fill in new level + */ + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + know[j][i] = mitem[j][i] = 0; + makemaze (x); + makeobject (x); + beenhere[x] = 1; + sethp (1); + positionplayer (); + checkgen (); /* wipe out any genocided monsters */ + +#if WIZID + if (wizard || x == 0) +#else + if (x == 0) +#endif + for (j = 0; j < MAXY; j++) + for (i = 0; i < MAXX; i++) + know[i][j] = KNOWALL; +} + + +/* +makemaze(level) +int level; + +subroutine to make the caverns for a given level. only walls are made. +*/ +static int mx, mxl, mxh, my, myl, myh, tmp2; + +static void +makemaze (int k) +{ + int i, j, tmp; + int z; + + if (k > 1 + && (rnd (17) <= 4 || k == MAXLEVEL - 1 + || k == MAXLEVEL + MAXVLEVEL - 1)) + { + /* read maze from data file */ + if (cannedlevel (k)) + { + return; + } + } + + if (k == 0) + { + + tmp = 0; + + } + else + { + + tmp = OWALL; + } + + for (i = 0; i < MAXY; i++) + { + for (j = 0; j < MAXX; j++) + { + + item[j][i] = tmp; + } + } + + if (k == 0) + { + return; + } + + eat (1, 1); + + if (k == 1) + { + + item[33][MAXY - 1] = OENTRANCE; + } + + /* now for open spaces -- not on level 10 */ + if (k != MAXLEVEL - 1) + { + tmp2 = rnd (3) + 3; + for (tmp = 0; tmp < tmp2; tmp++) + { + my = rnd (11) + 2; + myl = my - rnd (2); + myh = my + rnd (2); + if (k < MAXLEVEL) + { + mx = rnd (44) + 5; + mxl = mx - rnd (4); + mxh = mx + rnd (12) + 3; + z = 0; + } + else + { + mx = rnd (60) + 3; + mxl = mx - rnd (2); + mxh = mx + rnd (2); + z = makemonst (k); + } + for (i = mxl; i < mxh; i++) + for (j = myl; j < myh; j++) + { + item[i][j] = 0; + + mitem[i][j] = z; + if (mitem[i][j] != 0) + { + hitp[i][j] = monster[z].hitpoints; + } + } + } + } + if (k != MAXLEVEL - 1) + { + my = rnd (MAXY - 2); + for (i = 1; i < MAXX - 1; i++) + item[i][my] = 0; + } + if (k > 1) + treasureroom (k); +} + + + +/* +* function to eat away a filled in maze +*/ +void +eat (int xx, int yy) +{ + int dir, try; + + dir = rnd (4); + + try = 2; + + while (try) + { + switch (dir) + { + case 1: + if (xx <= 2) + break; /* west */ + if ((item[xx - 1][yy] != OWALL) || (item[xx - 2][yy] != OWALL)) + break; + item[xx - 1][yy] = item[xx - 2][yy] = 0; + eat (xx - 2, yy); + break; + + case 2: + if (xx >= MAXX - 3) + break; /* east */ + if ((item[xx + 1][yy] != OWALL) || (item[xx + 2][yy] != OWALL)) + break; + item[xx + 1][yy] = item[xx + 2][yy] = 0; + eat (xx + 2, yy); + break; + + case 3: + if (yy <= 2) + break; /* south */ + if ((item[xx][yy - 1] != OWALL) || (item[xx][yy - 2] != OWALL)) + break; + item[xx][yy - 1] = item[xx][yy - 2] = 0; + eat (xx, yy - 2); + break; + + case 4: + if (yy >= MAXY - 3) + break; /* north */ + if ((item[xx][yy + 1] != OWALL) || (item[xx][yy + 2] != OWALL)) + break; + item[xx][yy + 1] = item[xx][yy + 2] = 0; + eat (xx, yy + 2); + break; + }; + if (++dir > 4) + { + dir = 1; + --try; + } + } +} + +/* +* function to read in a maze from a data file +* +* Format of maze data file: 1st character = # of mazes in file (ascii digit) +* For each maze: 18 lines (1st 17 used) 67 characters per line +* +* Special characters in maze data file: +* +* # wall D door . random monster +* ~ eye of larn ! cure dianthroritis +* - random object +*/ +static int +cannedlevel (int k) +{ + char *row; + int i, j; + int it, arg, mit, marg; + + if (lopen (mazefile) < 0) + { + fprintf (stderr, "Can't open the maze data file\n"); + died (-282); + return (0); + } + i = lgetc (); + if (i <= '0') + { + died (-282); + return (0); + } + for (i = 18 * rund (i - '0'); i > 0; i--) + lgetl (); /* advance to desired maze */ + for (i = 0; i < MAXY; i++) + { + row = lgetl (); + for (j = 0; j < MAXX; j++) + { + it = mit = arg = marg = 0; + switch (*row++) + { + case '#': + it = OWALL; + break; + case 'D': + it = OCLOSEDDOOR; + arg = rnd (30); + break; + case '~': + if (k != MAXLEVEL - 1) + break; + it = OLARNEYE; + mit = rund (8) + DEMONLORD; + marg = monster[mit].hitpoints; + break; + case '!': + if (k != MAXLEVEL + MAXVLEVEL - 1) + break; + it = OPOTION; + arg = 21; + mit = DEMONLORD + 7; + marg = monster[mit].hitpoints; + break; + case '.': + if (k < MAXLEVEL) + break; + mit = makemonst (k + 1); + marg = monster[mit].hitpoints; + break; + case '-': + it = newobject (k + 1, &arg); + break; + }; + item[j][i] = it; + iarg[j][i] = arg; + mitem[j][i] = mit; + hitp[j][i] = marg; + +#if WIZID + know[j][i] = (wizard) ? KNOWALL : 0; +#else + know[j][i] = 0; +#endif + } + } + lrclose (); + return (1); +} + + + +/* +* function to make a treasure room on a level +* level 10's treasure room has the eye in it and demon lords +* level V3 has potion of cure dianthroritis and demon prince +*/ +static void +treasureroom (int lv) +{ + int tx, ty, xsize, ysize; + + for (tx = 1 + rnd (10); tx < MAXX - 10; tx += 10) + if ((lv == MAXLEVEL - 1) || (lv == MAXLEVEL + MAXVLEVEL - 1) + /*Increased this math to a 50 percent chance. -Gibbon */ + || rnd (10) == 5) + { + xsize = rnd (6) + 3; + ysize = rnd (3) + 3; + ty = rnd (MAXY - 9) + 1; /* upper left corner of room */ + if (lv == MAXLEVEL - 1 || lv == MAXLEVEL + MAXVLEVEL - 1) + troom (lv, xsize, ysize, tx = + tx + rnd (MAXX - 24), ty, rnd (3) + 6); + else + troom (lv, xsize, ysize, tx, ty, rnd (9)); + } +} + + + +/* +* subroutine to create a treasure room of any size at a given location +* room is filled with objects and monsters +* the coordinate given is that of the upper left corner of the room +*/ +static void +troom (int lv, int xsize, int ysize, int tx, int ty, int glyph) +{ + int i, j; + int tp1, tp2; + + for (j = ty - 1; j <= ty + ysize; j++) + for (i = tx - 1; i <= tx + xsize; i++) /* clear out space for room */ + item[i][j] = 0; + for (j = ty; j < ty + ysize; j++) + for (i = tx; i < tx + xsize; i++) /* now put in the walls */ + { + item[i][j] = OWALL; + mitem[i][j] = 0; + } + for (j = ty + 1; j < ty + ysize - 1; j++) + for (i = tx + 1; i < tx + xsize - 1; i++) /* now clear out interior */ + item[i][j] = 0; + + switch (rnd (2)) /* locate the door on the treasure room */ + { + case 1: + item[i = tx + rund (xsize)][j = ty + (ysize - 1) * rund (2)] = + OCLOSEDDOOR; + iarg[i][j] = glyph; /* on horizontal walls */ + break; + case 2: + item[i = tx + (xsize - 1) * rund (2)][j = ty + rund (ysize)] = + OCLOSEDDOOR; + iarg[i][j] = glyph; /* on vertical walls */ + break; + }; + + tp1 = playerx; + tp2 = playery; + playery = ty + (ysize >> 1); + + if (cdesc[HARDGAME] < 2) + { + for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2) + { + for (i = 0, j = rnd (6); i <= j; i++) + { + something (lv + 2); + createmonster (makemonst (lv + 1)); + } + } + } + else + { + for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2) + { + for (i = 0, j = rnd (4); i <= j; i++) + { + something (lv + 2); + createmonster (makemonst (lv + 3)); + } + } + playerx = tp1; + playery = tp2; + } +} + + +/* +* subroutine to create the objects in the maze for the given level +*/ +static void +makeobject (int j) +{ + int i; + + if (j == 0) + { + + /* entrance to dungeon */ + fillroom (OENTRANCE, 0); + + /* the DND STORE */ + fillroom (ODNDSTORE, 0); + + fillroom (OSCHOOL, 0); /* college of Larn */ + fillroom (OBANK, 0); /* 1st national bank of larn */ + fillroom (OVOLDOWN, 0); /* volcano shaft to temple */ + fillroom (OHOME, 0); /* the players home & family */ + fillroom (OTRADEPOST, 0); /* the trading post */ + fillroom (OLRS, 0); /* the larn revenue service */ + + return; + } + + if (j == MAXLEVEL) + fillroom (OVOLUP, 0); /* volcano shaft up from the temple */ + + /* make the fixed objects in the maze STAIRS */ + if ((j > 0) && (j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1)) + fillroom (OSTAIRSDOWN, 0); + if ((j > 1) && (j != MAXLEVEL)) + fillroom (OSTAIRSUP, 0); + + /* make the random objects in the maze */ + + fillmroom (rund (3), OBOOK, j); + fillmroom (rund (3), OALTAR, 0); + fillmroom (rund (3), OSTATUE, 0); + fillmroom (rund (3), OPIT, 0); + fillmroom (rund (3), OFOUNTAIN, 0); + fillmroom (rnd (3) - 2, OIVTELETRAP, 0); + fillmroom (rund (2), OTHRONE, 0); + fillmroom (rund (2), OMIRROR, 0); + fillmroom (rund (2), OTRAPARROWIV, 0); + fillmroom (rnd (3) - 2, OIVDARTRAP, 0); + fillmroom (rund (3), OCOOKIE, 0); + if (j == 1) + fillmroom (1, OCHEST, j); + else + fillmroom (rund (2), OCHEST, j); + if ((j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1)) + fillmroom (rund (2), OIVTRAPDOOR, 0); + if (j <= 10) + { + fillmroom ((rund (2)), ODIAMOND, rnd (10 * j + 1) + 10); + fillmroom (rund (2), ORUBY, rnd (6 * j + 1) + 6); + fillmroom (rund (2), OEMERALD, rnd (4 * j + 1) + 4); + fillmroom (rund (2), OSAPPHIRE, rnd (3 * j + 1) + 2); + } + for (i = 0; i < rnd (4) + 3; i++) + fillroom (OPOTION, newpotion ()); /* make a POTION */ + for (i = 0; i < rnd (5) + 3; i++) + fillroom (OSCROLL, newscroll ()); /* make a SCROLL */ + for (i = 0; i < rnd (12) + 11; i++) + fillroom (OGOLDPILE, 12 * rnd (j + 1) + (j << 3) + 10); /* make GOLD */ + if (j == 5) + fillroom (OBANK2, 0); /* branch office of the bank */ + froom (2, ORING, 0); /* a ring mail */ + froom (1, OSTUDLEATHER, 0); /* a studded leather */ + froom (3, OSPLINT, 0); /* a splint mail */ + froom (5, OSHIELD, rund (3)); /* a shield */ + froom (2, OBATTLEAXE, rund (3)); /* a battle axe */ + froom (5, OLONGSWORD, rund (3)); /* a long sword */ + froom (4, OREGENRING, rund (3)); /* ring of regeneration */ + froom (1, OPROTRING, rund (3)); /* ring of protection */ + froom (2, OSTRRING, 1 + rnd (3)); /* ring of strength */ + froom (7, OSPEAR, rnd (5)); /* a spear */ + froom (3, OORBOFDRAGON, 0); /* orb of dragon slaying */ + froom (4, OSPIRITSCARAB, 0); /* scarab of negate spirit */ + froom (4, OCUBEofUNDEAD, 0); /* cube of undead control */ + froom (2, ORINGOFEXTRA, 0); /* ring of extra regen */ + froom (3, ONOTHEFT, 0); /* device of antitheft */ + froom (2, OSWORDofSLASHING, 0); /* sword of slashing */ + if (cdesc[BESSMANN] == 0) + { + froom (4, OHAMMER, 0); /*Bessman's flailing hammer */ + cdesc[BESSMANN] = 1; + } + if (cdesc[HARDGAME] < 3 || (rnd (4) == 3)) + { + if (j > 3) + { + froom (3, OSWORD, 3); /* sunsword + 3 */ + froom (5, O2SWORD, rnd (4)); /* a two handed sword */ + froom (5, OHSWORD, rnd (4)); /* a longsword of Hymie */ + froom (3, OBELT, 4); /* belt of striking */ + froom (3, OENERGYRING, 3); /* energy ring */ + froom (4, OPLATE, 5); /* platemail + 5 */ + froom (3, OCLEVERRING, 1 + rnd (2)); /* ring of cleverness */ + } + } +} + + + +/* +* subroutine to fill in a number of objects of the same kind +*/ +static void +fillmroom (int n, int what, int arg) +{ + int i; + + for (i = 0; i < n; i++) + { + + fillroom (what, arg); + } +} + + + +static void +froom (int n, int itm, int arg) +{ + + if (rnd (151) < n) + { + + fillroom (itm, arg); + } +} + + +/* +* subroutine to put an object into an empty room +* uses a random walk +*/ +static void +fillroom (int what, int arg) +{ + int x, y; + +#ifdef EXTRA + cdesc[FILLROOM]++; +#endif + + x = rnd (MAXX - 2); + y = rnd (MAXY - 2); + + while (item[x][y]) + { + +#ifdef EXTRA + /* count up these random walks */ + cdesc[RANDOMWALK]++; +#endif + + x += rnd (3) - 2; + y += rnd (3) - 2; + + /* clamp location to within map border */ + if (x > MAXX - 2) + x = 1; + if (x < 1) + x = MAXX - 2; + if (y > MAXY - 2) + y = 1; + if (y < 1) + y = MAXY - 2; + } + + item[x][y] = what; + iarg[x][y] = arg; +} + + + + +/* +subroutine to put monsters into an empty room without walls or other +monsters +*/ +int +fillmonst (int what) +{ + int x, y, trys; + + for (trys = 5; trys > 0; --trys) /* max # of creation attempts */ + { + x = rnd (MAXX - 2); + y = rnd (MAXY - 2); + if ((item[x][y] == 0) && (mitem[x][y] == 0) + && ((playerx != x) || (playery != y))) + { + mitem[x][y] = what; + know[x][y] &= ~KNOWHERE; + hitp[x][y] = monster[what].hitpoints; + return (0); + } + } + + return -1; /* creation failure */ +} + + + +/* +* creates an entire set of monsters for a level +* must be done when entering a new level +* if sethp(1) then wipe out old monsters else leave them there +*/ +static void +sethp (int flg) +{ + int i, j; + + if (flg) + { + + for (i = 0; i < MAXY; i++) + { + for (j = 0; j < MAXX; j++) + { + + stealth[j][i] = 0; + } + } + } + + /* if teleported and found level 1 then know level we are on */ + if (level == 0) + { + + cdesc[TELEFLAG] = 0; + + return; + } + + if (flg) + { + + j = rnd (12) + 2 + (level >> 1); + + } + else + { + + j = (level >> 1) + 1; + } + + for (i = 0; i < j; i++) + { + + fillmonst (makemonst (level)); + } +} + + + +/* +* Function to destroy all genocided monsters on the present level +*/ +static void +checkgen (void) +{ + int x, y; + + for (y = 0; y < MAXY; y++) + { + for (x = 0; x < MAXX; x++) + { + + if (monster[mitem[x][y]].genocided) + { + + /* no more monster */ + mitem[x][y] = 0; + } + } + } +} diff --git a/src/data.c b/src/data.c new file mode 100644 index 0000000..1f7cc6e --- /dev/null +++ b/src/data.c @@ -0,0 +1,714 @@ +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include +#include "includes/io.h" +#include "includes/moreobj.h" + +/* +classname[cdesc[LEVEL]-1] gives the correct name of the players experience level +*/ +static char aa1[] = " mighty evil master"; +static char aa2[] = "apprentice demi-god"; +static char aa3[] = " minor demi-god "; +static char aa4[] = " major demi-god "; +static char aa5[] = " minor deity "; +static char aa6[] = " major deity "; +static char aa7[] = " novice guardian "; +static char aa8[] = "apprentice guardian"; +static char aa9[] = " The Creator "; +char *classname[] = { " novice explorer ", "apprentice explorer", " practiced explorer", /* -3 */ + " expert explorer ", " novice adventurer", " adventurer ", /* -6 */ + "apprentice conjurer", " conjurer ", " master conjurer ", /* -9 */ + " apprentice mage ", " mage ", " experienced mage ", /* -12 */ + " master mage ", " apprentice warlord", " novice warlord ", /* -15 */ + " expert warlord ", " master warlord ", " apprentice gorgon ", /* -18 */ + " gorgon ", " practiced gorgon ", " master gorgon ", /* -21 */ + " demi-gorgon ", " evil master ", " great evil master ", /* -24 */ + aa1, aa1, aa1, /* -27 */ + aa1, aa1, aa1, /* -30 */ + aa1, aa1, aa1, /* -33 */ + aa1, aa1, aa1, /* -36 */ + aa1, aa1, aa1, /* -39 */ + aa2, aa2, aa2, /* -42 */ + aa2, aa2, aa2, /* -45 */ + aa2, aa2, aa2, /* -48 */ + aa3, aa3, aa3, /* -51 */ + aa3, aa3, aa3, /* -54 */ + aa3, aa3, aa3, /* -57 */ + aa4, aa4, aa4, /* -60 */ + aa4, aa4, aa4, /* -63 */ + aa4, aa4, aa4, /* -66 */ + aa5, aa5, aa5, /* -69 */ + aa5, aa5, aa5, /* -72 */ + aa5, aa5, aa5, /* -75 */ + aa6, aa6, aa6, /* -78 */ + aa6, aa6, aa6, /* -81 */ + aa6, aa6, aa6, /* -84 */ + aa7, aa7, aa7, /* -87 */ + aa8, aa8, aa8, /* -90 */ + aa8, aa8, aa8, /* -93 */ + " earth guardian ", " air guardian ", " fire guardian ", /* -96 */ + " water guardian ", " time guardian ", " ethereal guardian ", /* -99 */ + aa9, aa9, aa9, /* -102 */ +}; + +/* +table of experience needed to be a certain level of player +skill[cdesc[LEVEL]] is the experience required to attain the next level +*/ +#define MEG 1000000 +long skill[] = { + 0, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, /* 1-11 */ + 10240, 20480, 40960, 100000, 200000, 400000, 700000, 1 * MEG, /* 12-19 */ + 2 * MEG, 3 * MEG, 4 * MEG, 5 * MEG, 6 * MEG, 8 * MEG, 10 * MEG, /* 20-26 */ + 12 * MEG, 14 * MEG, 16 * MEG, 18 * MEG, 20 * MEG, 22 * MEG, 24 * MEG, 26 * MEG, 28 * MEG, /* 27-35 */ + 30 * MEG, 32 * MEG, 34 * MEG, 36 * MEG, 38 * MEG, 40 * MEG, 42 * MEG, 44 * MEG, 46 * MEG, /* 36-44 */ + 48 * MEG, 50 * MEG, 52 * MEG, 54 * MEG, 56 * MEG, 58 * MEG, 60 * MEG, 62 * MEG, 64 * MEG, /* 45-53 */ + 66 * MEG, 68 * MEG, 70 * MEG, 72 * MEG, 74 * MEG, 76 * MEG, 78 * MEG, 80 * MEG, 82 * MEG, /* 54-62 */ + 84 * MEG, 86 * MEG, 88 * MEG, 90 * MEG, 92 * MEG, 94 * MEG, 96 * MEG, 98 * MEG, 100 * MEG, /* 63-71 */ + 105 * MEG, 110 * MEG, 115 * MEG, 120 * MEG, 125 * MEG, 130 * MEG, 135 * MEG, 140 * MEG, /* 72-79 */ + 145 * MEG, 150 * MEG, 155 * MEG, 160 * MEG, 165 * MEG, 170 * MEG, 175 * MEG, 180 * MEG, /* 80-87 */ + 185 * MEG, 190 * MEG, 195 * MEG, 200 * MEG, 210 * MEG, 220 * MEG, 230 * MEG, 240 * MEG, /* 88-95 */ + 250 * MEG, 260 * MEG, 270 * MEG, 280 * MEG, 290 * MEG, 300 * MEG /* 96-101 */ +}; + +#undef MEG + +char *lpbuf, *lpnt, *inbuffer, *lpend; /* input/output pointers to the buffers */ + +struct cel *cell; /* pointer to the dungeon storage */ + +int hitp[MAXX][MAXY]; /* monster hp on level */ +int iarg[MAXX][MAXY]; /* arg for the item array */ +int item[MAXX][MAXY]; /* objects in maze if any */ +int know[MAXX][MAXY]; /* 1 or 0 if here before */ +int mitem[MAXX][MAXY]; /* monster item array */ +int stealth[MAXX][MAXY]; /* 0=sleeping 1=awake monst */ +char lastmonst[40]; /* this has the name of the current monster */ +int beenhere[MAXLEVEL + MAXVLEVEL]; /* 1 if have been on this level */ +int VERSION = VER; /* this is the present version # of the program */ +int SUBVERSION = SUBVER; +int predostuff = 0; /* 2 means that the trap handling routines must do a + showplayer() after a trap. 0 means don't showplayer() + 0 - we are in create player screen + 1 - we are in welcome screen + 2 - we are in the normal game */ + +char logname[LOGNAMESIZE]; /* the player's name */ + +int cheat = 0; /* 1 if the player has fudged save file */ +int level = 0; /* cavelevel player is on = cdesc[CAVELEVEL] */ +int wizard = 0; /* the wizard mode flag */ +int lastnum = 0; /* the number of the monster last hitting player */ +int hitflag = 0; /* flag for if player has been hit when running */ +int hit2flag = 0; /* flag for if player has been hit when running */ +int hit3flag = 0; /* flag for if player has been hit flush input */ +int playerx, playery; /* the room on the present level of the player */ +int lastpx, lastpy; /* 0 --- MAXX-1 or 0 --- MAXY-1 */ +int oldx, oldy; +int prayed = 1; /* did player pray at an altar (command mode)? needs + to be saved, but I don't want to add incompatibility + right now. KBR 1/11/90 */ +int lasthx = 0, lasthy = 0; /* location of monster last hit by player */ +unsigned long lrandx = 33601; /* the random number seed */ +time_t initialtime = 0; /* time playing began */ +long gtime = 0; /* the clock for the game */ +long outstanding_taxes = 0; /* present tax bill from score file */ +long cdesc[100], cbak[100]; /* the character description arrays */ +int enable_scroll = 0; /* constant for enabled/disabled scrolling regn */ +char aborted[] = " aborted"; +struct sphere *spheres = 0; /*pointer to linked list for spheres of annihilation */ + +char *levelname[] = + { " H", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", "V1", + "V2", "V3" +}; + +/* This char array was too short. MAXOBJECT is defined as 92 and having this + as +2 (making it 94) was too short to hold the entire array. +36 fixes this as it + is the minimum required to hold all objects. -Gibbon +*/ + +char objnamelist[MAXOBJECT + 37] = ".AT_P<_F&^+M=>_$$f*OD#~][[)))(((||||||||{?!BC}o:;,@@@@EVV))([[]]](^.[H***.^^.S.tsTLc_____________________________________________"; +char monstnamelist[] = ".BGHJKOScjtAELNQRZabhiCTYdegmvzFWflorXV.pqsyUkMwDDPxnDDuD........,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"; + +char floorc = '.'; +char wallc = '#'; +char *objectname[] = + { 0, "a holy altar", "a handsome jewel encrusted throne", "the orb", + "a pit", + "a staircase leading upwards", "an elevator going up", + "a bubbling fountain", + "a great marble statue", "a teleport trap", "the college of Larn", + "a mirror", "the DND store", "a staircase going down", + "an elevator going down", + "the bank of Larn", "the 5th branch of the Bank of Larn", + "a dead fountain", "gold", "an open door", "a closed door", + "a wall", "The Eye of Larn", "plate mail", "chain mail", "leather armor", + "a sword of slashing", "Bessman's flailing hammer", "a sunsword", + "a two handed sword", "a spear", "a dagger", + "ring of extra regeneration", "a ring of regeneration", + "a ring of protection", + "an energy ring", "a ring of dexterity", "a ring of strength", + "a ring of cleverness", "a ring of increase damage", "a belt of striking", + "a magic scroll", "a magic potion", "a book", "a chest", + "an amulet of invisibility", "an orb of dragon slaying", + "a scarab of negate spirit", "a cube of undead control", + "device of theft prevention", "a brilliant diamond", "a ruby", + "an enchanting emerald", "a sparkling sapphire", "the dungeon entrance", + "a volcanic shaft leaning downward", "the base of a volcanic shaft", + "a battle axe", "a longsword", "a longsword of Hymie", "ring mail", + "studded leather armor", + "splint mail", "plate armor", "stainless plate armor", + "Great Sword of Death", + "an arrow trap", "an arrow trap", "a shield", "your home", + "gold", "gold", "gold", "a dart trap", + "a dart trap", "a trapdoor", "a trapdoor", "the local trading post", + "a teleport trap", "a massive throne", + "a sphere of annihilation", "a handsome jewel encrusted throne", + "the Larn Revenue Service", "a fortune cookie", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" +}; + +/* +* for the monster data +* +* array to do rnd() to create monsters <= a given level +*/ +int monstlevel[] = { 5, 11, 17, 22, 27, 33, 39, 42, 46, 50, 53, 56, 59 }; + +struct monst monster[] = { + /* NAME LV AC DAM ATT DEF GEN INT GOLD HP EXP + ----------------------------------------------------------------- */ + {"", 0, 0, 0, 0, 0, 0, 3, 0, 0, 0}, + {"bat", 1, 0, 1, 0, 0, 0, 3, 0, 1, 1}, + {"gnome", 1, 10, 1, 0, 0, 0, 8, 30, 2, 2}, + {"hobgoblin", 1, 14, 2, 0, 0, 0, 5, 25, 3, 2}, + {"jackal", 1, 17, 1, 0, 0, 0, 4, 0, 1, 1}, + {"kobold", 1, 20, 1, 0, 0, 0, 7, 10, 1, 1}, + + {"orc", 2, 12, 1, 0, 0, 0, 9, 40, 4, 2}, + {"snake", 2, 15, 1, 0, 0, 0, 3, 0, 3, 1}, + {"giant centipede", 2, 14, 0, 4, 0, 0, 3, 0, 1, 2}, + {"jaculi", 2, 20, 1, 0, 0, 0, 3, 0, 2, 1}, + {"troglodyte", 2, 10, 2, 0, 0, 0, 5, 80, 4, 3}, + {"giant ant", 2, 8, 1, 4, 0, 0, 4, 0, 5, 5}, + + /* NAME LV AC DAM ATT DEF GEN INT GOLD HP EXP + ----------------------------------------------------------------- */ + + {"floating eye", 3, 8, 1, 0, 0, 0, 3, 0, 5, 2}, + {"leprechaun", 3, 3, 0, 8, 0, 0, 3, 1500, 13, 45}, + {"nymph", 3, 3, 0, 14, 0, 0, 9, 0, 18, 45}, + {"quasit", 3, 5, 3, 0, 0, 0, 3, 0, 10, 15}, + {"rust monster", 3, 4, 0, 1, 0, 0, 3, 0, 18, 25}, + {"zombie", 3, 12, 2, 0, 0, 0, 3, 0, 6, 7}, + + {"assassin bug", 4, 9, 3, 0, 0, 0, 3, 0, 20, 15}, + {"bugbear", 4, 5, 4, 15, 0, 0, 5, 40, 20, 35}, + {"hell hound", 4, 5, 2, 2, 0, 0, 6, 0, 16, 35}, + {"ice lizard", 4, 11, 2, 10, 0, 0, 6, 50, 16, 25}, + {"centaur", 4, 6, 4, 0, 0, 0, 10, 40, 24, 45}, + + /* NAME LV AC DAM ATT DEF GEN INT GOLD HP EXP + ----------------------------------------------------------------- */ + + {"troll", 5, 4, 5, 0, 0, 0, 9, 80, 50, 300}, + {"yeti", 5, 6, 4, 0, 0, 0, 5, 50, 35, 100}, + {"white dragon", 5, 2, 4, 5, 0, 0, 16, 500, 55, 1000}, + {"elf", 5, 8, 1, 0, 0, 0, 15, 50, 22, 35}, + {"gelatinous cube", 5, 9, 1, 0, 0, 0, 3, 0, 22, 45}, + + {"metamorph", 6, 7, 3, 0, 0, 0, 3, 0, 30, 40}, + {"vortex", 6, 4, 3, 0, 0, 0, 3, 0, 30, 55}, + {"ziller", 6, 15, 3, 0, 0, 0, 3, 0, 30, 35}, + {"violet fungi", 6, 12, 3, 0, 0, 0, 3, 0, 38, 100}, + {"wraith", 6, 3, 1, 6, 0, 0, 3, 0, 30, 325}, + {"forvalaka", 6, 2, 5, 0, 0, 0, 7, 0, 50, 280}, + + /* NAME LV AC DAM ATT DEF GEN INT GOLD HP EXP + ----------------------------------------------------------------- */ + + {"lama nobe", 7, 7, 3, 0, 0, 0, 6, 0, 35, 80}, + {"osequip", 7, 4, 3, 16, 0, 0, 4, 0, 35, 100}, + {"rothe", 7, 15, 5, 0, 0, 0, 3, 100, 50, 250}, + {"xorn", 7, 0, 6, 0, 0, 0, 13, 0, 60, 300}, + {"vampire", 7, 3, 4, 6, 0, 0, 17, 0, 50, 1000}, + {"invisible stalker", 7, 3, 6, 0, 0, 0, 5, 0, 50, 350}, + + {"poltergeist", 8, 1, 4, 0, 0, 0, 3, 0, 50, 450}, + {"disenchantress", 8, 3, 0, 9, 0, 0, 3, 0, 50, 500}, + {"shambling mound", 8, 2, 5, 0, 0, 0, 6, 0, 45, 400}, + {"yellow mold", 8, 12, 4, 0, 0, 0, 3, 0, 35, 250}, + {"umber hulk", 8, 3, 7, 11, 0, 0, 14, 0, 65, 600}, + + /* NAME LV AC DAM ATT DEF GEN INT GOLD HP EXP + ----------------------------------------------------------------- */ + + {"gnome king", 9, -1, 10, 0, 0, 0, 18, 2000, 100, 3000}, + {"mimic", 9, 5, 6, 0, 0, 0, 8, 0, 55, 99}, + {"water lord", 9, -10, 15, 7, 0, 0, 20, 0, 150, 15000}, + {"bronze dragon", 9, 2, 9, 3, 0, 0, 16, 300, 80, 4000}, + {"green dragon", 9, 3, 8, 10, 0, 0, 15, 200, 70, 2500}, + {"purple worm", 9, -1, 11, 0, 0, 0, 3, 100, 120, 15000}, + {"xvart", 9, -2, 12, 0, 0, 0, 13, 0, 90, 1000}, + + {"spirit naga", 10, -20, 12, 12, 0, 0, 23, 0, 95, 20000}, + {"silver dragon", 10, -1, 12, 3, 0, 0, 20, 700, 100, 10000}, + {"platinum dragon", 10, -5, 15, 13, 0, 0, 22, 1000, 130, 24000}, + {"green urchin", 10, -3, 12, 0, 0, 0, 3, 0, 85, 5000}, + {"red dragon", 10, -2, 13, 3, 0, 0, 19, 800, 110, 14000}, + + {"type I demon lord", 12, -30, 18, 0, 0, 0, 20, 0, 140, 50000}, + {"type II demon lord", 13, -30, 18, 0, 0, 0, 21, 0, 160, 75000}, + {"type III demon lord", 14, -30, 18, 0, 0, 0, 22, 0, 180, 100000}, + {"type IV demon lord", 15, -35, 20, 0, 0, 0, 23, 0, 200, 125000}, + {"type V demon lord", 16, -40, 22, 0, 0, 0, 24, 0, 220, 150000}, + {"type VI demon lord", 17, -45, 24, 0, 0, 0, 25, 0, 240, 175000}, + {"type VII demon lord", 18, -70, 27, 6, 0, 0, 26, 0, 260, 200000}, + {"demon prince", 25, -127, 30, 6, 0, 0, 28, 0, 345, 300000} + + /* NAME LV AC DAM ATT DEF GEN INT GOLD HP EXP + --------------------------------------------------------------------- */ +}; + +/* name array for scrolls */ + +char scrollname[MAXSCROLL + 1][MAXSCROLLNAME] = { + "\0enchant armor", + "\0enchant weapon", + "\0enlightenment", + "\0blank paper", + "\0create monster", + "\0create artifact", + "\0aggravate monsters", + "\0time warp", + "\0teleportation", + "\0expanded awareness", + "\0haste monsters", + "\0monster healing", + "\0spirit protection", + "\0undead protection", + "\0stealth", + "\0magic mapping", + "\0hold monsters", + "\0gem perfection", + "\0spell extension", + "\0identify", + "\0remove curse", + "\0annihilation", + "\0pulverization", + "\0life protection", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0zzzzzzzzzzzzzz" /* sentinel, for the sorted known objects inventory */ +}; + +/* name array for magic potions */ +char potionname[MAXPOTION + 1][MAXPOTIONNAME] = { + "\0sleep", + "\0healing", + "\0raise level", + "\0increase ability", + "\0wisdom", + "\0strength", + "\0raise charisma", + "\0dizziness", + "\0learning", + "\0object detection", + "\0monster detection", + "\0forgetfulness", + "\0water", + "\0blindness", + "\0confusion", + "\0heroism", + "\0sturdiness", + "\0giant strength", + "\0fire resistance", + "\0treasure finding", + "\0instant healing", + " cure dianthroritis", + "\0poison", + "\0see invisible", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0 ", + "\0zzzzzzzzzzzzzz" /* sentinel, for the sorted known objects inventory */ +}; + + +/* +spell data +*/ +int spelknow[SPNUM]; +int splev[] = { 1, 4, 9, 14, 18, 22, 26, 29, 32, 35, 37, 37, 37, 37, 37 }; + +char *spelcode[SPNUM + 1] = { + "pro", "mle", "dex", "sle", "chm", "ssp", + "web", "str", "enl", "hel", "cbl", "cre", "pha", "inv", + "bal", "cld", "ply", "can", "has", "ckl", "vpr", + "dry", "lit", "drl", "glo", "flo", "fgr", + "sca", "hld", "stp", "tel", "mfi", /* 31 */ + "sph", "gen", "sum", "wtw", "alt", "per", "zzz" +}; + +char *spelname[] = { + "protection", "magic missile", "dexterity", + "sleep", "charm monster", "sonic spear", + + "web", "strength", "enlightenment", + "healing", "cure blindness", "create monster", + "phantasmal forces", "invisibility", + + "fireball", "cold", "polymorph", + "cancellation", "haste self", "cloud kill", + "vaporize rock", + + "dehydration", "lightning", "drain life", + "invulnerability", "flood", "finger of death", + + "scare monster", "hold monster", "time stop", + "teleport away", "magic fire", + + "sphere of annihilation", "genocide", "summon demon", + "walk through walls", "alter reality", "permanence", + "" +}; + +char *speldescript[] = { + /* 1 */ + "generates a +2 protection field", + "creates and hurls a magic missile equivalent to a + 1 magic arrow", + "adds +2 to the casters dexterity", + "causes some monsters to go to sleep", + "some monsters may be awed at your magnificence", + "causes your hands to emit a screeching sound toward what they point", + /* 7 */ + "causes strands of sticky thread to entangle an enemy", + "adds +2 to the casters strength for a short term", + "the caster becomes aware of things around him", + "restores some hp to the caster", + "restores sight to one so unfortunate as to be blinded", + "creates a monster near the caster appropriate for the location", + "creates illusions, and if believed, monsters die", + "the caster becomes invisible", + /* 15 */ + "makes a ball of fire that burns on what it hits", + "sends forth a cone of cold which freezes what it touches", + "you can find out what this does for yourself", + "negates the ability of a monster to use his special abilities", + "speeds up the casters movements", + "creates a fog of poisonous gas which kills all that is within it", + "this changes rock to air", + /* 22 */ + "dries up water in the immediate vicinity", + "you finger will emit a lightning bolt when this spell is cast", + "subtracts hit points from both you and a monster", + "this globe helps to protect the player from physical attack", + "this creates an avalanche of H2O to flood the immediate chamber", + "this is a holy spell and calls upon your god to back you up", + /* 28 */ + "terrifies the monster so that hopefully he wont hit the magic user", + "the monster is frozen in his tracks if this is successful", + "all movement in the caverns ceases for a limited duration", + "moves a particular monster around in the dungeon (hopefully away from you)", + "this causes a curtain of fire to appear all around you", + /* 33 */ + "anything caught in this sphere is instantly killed. Warning -- dangerous", + "eliminates a species of monster from the game -- use sparingly", + "summons a demon who hopefully helps you out", + "allows the player to walk through walls for a short period of time", + "god only knows what this will do", + "makes a character spell permanent, i. e. protection, strength, etc.", + "" +}; + +int spelweird[MAXMONST + 8][SPNUM] = { + /* p m d s c s w s e h c c p i b c p c h c v d l d g f f s h s t m s g s w a p */ + /* r l e l h s e t n e b r h n a l l a a k p r i r l l g c l t e f p e u t l e */ + /* o e x e m p b r l l l e a v l d y n s l r y t l o o r a d p l i h n m w t r */ + + + /* bat */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* gnome */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* hobgoblin */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* jackal */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* kobold */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* orc */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* snake */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /*giant centipede */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* jaculi */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* troglodyte */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* giant ant */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* floating eye */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* leprechaun */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* nymph */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* quasit */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* rust monster */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* zombie */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* assassin bug */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* bugbear */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* hell hound */ {0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* ice lizard */ {0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0}, + /* centaur */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* troll */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* yeti */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* white dragon */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 15, 0, + 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0}, + + /* elf */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /*gelatinous cube */ {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* metamorph */ {0, 13, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* vortex */ {0, 13, 0, 0, 0, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0}, + /* ziller */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* violet fungi */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* wraith */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* forvalaka */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* lama nobe */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* osequip */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* rothe */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* xorn */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* vampire */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /*invisible staker */ {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* poltergeist */ {0, 13, 0, 8, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* disenchantress */ {0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /*shambling mound */ {0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* yellow mold */ {0, 0, 0, 8, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* umber hulk */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* gnome king */ {0, 7, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* mimic */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* water lord */ {0, 13, 0, 8, 3, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, + 0, 4, 0, 0, 0, 0, 0, 16, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* bronze dragon */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* green dragon */ {0, 7, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* purple worm */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* xvart */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* spirit naga */ {0, 13, 0, 8, 3, 4, 1, 0, 0, 0, 0, 0, 0, 5, 0, 4, 9, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* silver dragon */ {0, 6, 0, 9, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /*platinum dragon */ {0, 7, 0, 9, 0, 0, 11, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0}, + /* green urchin */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + /* red dragon */ {0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}, + + /* p m d s c s w s e h c c p i b c p c h c v d l d g f f s h s t m s g s w a p */ + /* r l e l h s e t n e b r h n a l l a a k p r i r l l g c l t e f p e u t l e */ + /* o e x e m p b r l l l e a v l d y n s l r y t l o o r a d p l i h n m w t r */ + + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0}, + /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0}, + /* demon prince */ {0, 7, 0, 4, 3, 9, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, + 0, 4, 0, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 9, 0, 0, 0, + 0, 0} + +}; + +char *spelmes[] = { "", +/* 1 */ "the web had no effect on the %s", +/* 2 */ "the %s changed shape to avoid the web", +/* 3 */ "the %s isn't afraid of you", +/* 4 */ "the %s isn't affected", +/* 5 */ "the %s can see you with his infravision", +/* 6 */ "the %s vaporizes your missile", +/* 7 */ "your missile bounces off the %s", +/* 8 */ "the %s doesn't sleep", +/* 9 */ "the %s resists", +/* 10 */ "the %s can't hear the noise", +/* 11 */ "the %s's tail cuts it free of the web", +/* 12 */ "the %s burns through the web", +/* 13 */ "your missiles pass right through the %s", +/* 14 */ "the %s sees through your illusions", +/* 15 */ "the %s loves the cold!", +/* 16 */ "the %s loves the water!" +}; + +/* +* function to create scroll numbers with appropriate probability of +* occurrence +* +* 0 - armor 1 - weapon 2 - enlightenment 3 - paper +* 4 - create monster 5 - create item 6 - aggravate 7 - time warp +* 8 - teleportation 9 - expanded awareness 10 - haste monst +* 11 - heal monster 12 - spirit protection 13 - undead protection +* 14 - stealth 15 - magic mapping 16 - hold monster +* 17 - gem perfection 18 - spell extension 19 - identify +* 20 - remove curse 21 - annihilation 22 - pulverization +* 23 - life protection +*/ +int scprob[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, + 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, + 15, 15, 16, 16, 16, 17, 17, 18, 18, 19, 19, 19, 20, 20, 20, 20, 21, 22, + 22, 22, 23 +}; + +/* +* function to return a potion number created with appropriate probability +* of occurrence +* +* 0 - sleep 1 - healing 2 - raise level +* 3 - increase ability 4 - gain wisdom 5 - gain strength +* 6 - increase charisma 7 - dizziness 8 - learning +* 9 - object detection 10 - monster detection 11 - forgetfulness +* 12 - water 13 - blindness 14 - confusion +* 15 - heroism 16 - sturdiness 17 - giant strength +* 18 - fire resistance 19 - treasure finding 20 - instant healing +* 21 - cure dianthroritis 22 - poison 23 - see invisible +*/ +int potprob[] = { 0, 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9, + 10, 10, 10, 11, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 19, + 20, 20, 22, 22, 23, 23 +}; + +int nlpts[] = { 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7 }; +int nch[] = { 0, 0, 0, 1, 1, 1, 2, 2, 3, 4 }; +int nplt[] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 4 }; +int ndgg[] = { 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5 }; +int nsw[] = { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3 }; diff --git a/src/diag.c b/src/diag.c new file mode 100644 index 0000000..349acbe --- /dev/null +++ b/src/diag.c @@ -0,0 +1,508 @@ +/* diag.c */ +#include +#include +#include +#include +#include +#include + +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/global.h" +#include "includes/io.h" +#include "includes/savelev.h" +#include "includes/scores.h" +#include "includes/store.h" +#include "includes/sysdep.h" +#include "includes/create.h" +#include "includes/diag.h" + +#ifdef ANTICHEAT +static void greedy (void); +static void fsorry (void); +static void fcheat (void); +#endif + + +/* +*************************** +DIAG -- dungeon diagnostics +*************************** + +subroutine to print out data for debugging +*/ +#ifdef EXTRA +static int rndcount[16]; + +int +diag (void) +{ + int i, j; + int hit, dam; + + cursors (); + lwclose (); + if (lcreat (diagfile) < 0) /* open the diagnostic file */ + { + lcreat ((char *) 0); + lprcat ("\ndiagnostic failure\n"); + return (-1); + } + + write (1, "\nDiagnosing . . .\n", 18); + lprcat ("\n\nBeginning of DIAG diagnostics ----------\n"); + + /* for the character attributes */ + + lprintf ("\n\nPlayer attributes:\n\nHit points: %2d(%2d)", (int) cdesc[HP], + (int) cdesc[HPMAX]); + lprintf + ("\ngold: %d Experience: %d Character level: %d Level in caverns: %d", + (int) cdesc[GOLD], (int) cdesc[EXPERIENCE], (int) cdesc[LEVEL], + (int) level); + lprintf ("\nTotal types of monsters: %d", (int) MAXMONST + 8); + + lprcat ("\f\nHere's the dungeon:\n\n"); + + i = level; + for (j = 0; j < MAXLEVEL + MAXVLEVEL; j++) + { + newcavelevel (j); + lprintf ("\nMaze for level %s:\n", levelname[level]); + diagdrawscreen (); + } + newcavelevel (i); + + lprcat ("\f\nNow for the monster data:\n\n"); + lprcat + (" Monster Name LEV AC DAM ATT DEF GOLD HP EXP \n"); + lprcat + ("--------------------------------------------------------------------------\n"); + for (i = 0; i <= MAXMONST + 8; i++) + { + lprintf ("%19s %2d %3d ", monster[i].name, (int) monster[i].level, + (int) monster[i].armorclass); + lprintf (" %3d %3d %3d ", (int) monster[i].damage, + (int) monster[i].attack, (int) monster[i].defense); + lprintf ("%6d %3d %6d\n", (int) monster[i].gold, + (int) monster[i].hitpoints, (int) monster[i].experience); + } + + lprcat ("\n\nHere's a Table for the to hit percentages\n"); + lprcat + ("\n We will be assuming that players level = 2 * monster level"); + lprcat ("\n and that the players dexterity and strength are 16."); + lprcat + ("\n to hit: if (rnd(22) < (2[monst AC] + your level + dex + WC/8 -1)/2) then hit"); + lprcat ("\n damage = rund(8) + WC/2 + STR - cdesc[HARDGAME] - 4"); + lprcat ("\n to hit: if rnd(22) < to hit then player hits\n"); + lprcat + ("\n Each entry is as follows: to hit / damage / number hits to kill\n"); + lprcat ("\n monster WC = 4 WC = 20 WC = 40"); + lprcat + ("\n---------------------------------------------------------------"); + for (i = 0; i <= MAXMONST + 8; i++) + { + hit = 2 * monster[i].armorclass + 2 * monster[i].level + 16; + dam = 16 - cdesc[HARDGAME]; + lprintf ("\n%20s %2d/%2d/%2d %2d/%2d/%2d %2d/%2d/%2d", + monster[i].name, + (int) (hit / 2), (int) max (0, dam + 2), + (int) (monster[i].hitpoints / (dam + 2) + 1), + (int) ((hit + 2) / 2), (int) max (0, dam + 10), + (int) (monster[i].hitpoints / (dam + 10) + 1), + (int) ((hit + 5) / 2), (int) max (0, dam + 20), + (int) (monster[i].hitpoints / (dam + 20) + 1)); + } + + lprcat ("\n\nHere's the list of available potions:\n\n"); + for (i = 0; i < MAXPOTION; i++) + lprintf ("%20s\n", &potionname[i][1]); + lprcat ("\n\nHere's the list of available scrolls:\n\n"); + for (i = 0; i < MAXSCROLL; i++) + lprintf ("%20s\n", &scrollname[i][1]); + lprcat ("\n\nHere's the spell list:\n\n"); + lprcat ("spell name description\n"); + lprcat + ("-------------------------------------------------------------------------------------------\n\n"); + for (j = 0; j < SPNUM; j++) + { + lprc (' '); + lprcat (spelcode[j]); + lprintf (" %21s %s\n", spelname[j], speldescript[j]); + } + + lprcat ("\n\nFor the cdesc[] array:\n"); + for (j = 0; j < 100; j += 10) + { + lprintf ("\nc[%2d] = ", (int) j); + for (i = 0; i < 9; i++) + lprintf ("%5d ", (int) cdesc[i + j]); + } + + lprcat ("\n\nTest of random number generator ----------------"); + lprcat ("\n for 25,000 calls divided into 16 slots\n\n"); + + for (i = 0; i < 16; i++) + rndcount[i] = 0; + for (i = 0; i < 25000; i++) + rndcount[rund (16)]++; + for (i = 0; i < 16; i++) + { + lprintf (" %d: %5d", i,(int) rndcount[i]); + if (i == 7) + lprc ('\n'); + } + + lprcat ("Done\n\n"); + lwclose (); + lcreat ((char *) 0); + lprcat ("Done Diagnosing . . ."); + return (0); +} + + +/* +subroutine to draw the whole screen as the player knows it +*/ +void +diagdrawscreen (void) +{ + int i, j, k; + + for (i = 0; i < MAXY; i++) + + /* for the east west walls of this line */ + { + for (j = 0; j < MAXX; j++) + if (k = mitem[j][i]) + lprc (monstnamelist[k]); + else + lprc (objnamelist[item[j][i]]); + lprc ('\n'); + } +} +#endif + +/* +to save the game in a file +*/ +static time_t zzz = 0; + +int +savegame (char *fname) +{ + int i, k; + struct sphere *sp; + time_t temptime; + + lflush (); + savelevel (); + ointerest (); + if (lcreat (fname) < 0) + { + lcreat ((char *) 0); + lprintf ("\nCan't open file <%s> to save game\n", fname); + return (-1); + } + + set_score_output (); + + lwrite (logname, LOGNAMESIZE); + + lwrite ((char *) beenhere, (sizeof (int) * (MAXLEVEL + MAXVLEVEL))); + + for (k = 0; k < MAXLEVEL + MAXVLEVEL; k++) + { + if (beenhere[k]) + lwrite ((char *) &cell[(int) k * MAXX * MAXY], + sizeof (struct cel) * MAXY * MAXX); + } + + lwrite ((char *) &cdesc[0], 100 * sizeof (long)); + + lwrite ((char *) >ime, 1 * sizeof (long)); + lprint (level); + lprint (playerx); + lprint (playery); + + lwrite ((char *) iven, 26 * sizeof (int)); + lwrite ((char *) ivenarg, 26 * sizeof (int)); + + for (k = 0; k < MAXSCROLL; k++) + { + + lprc (scrollname[k][0]); + } + + for (k = 0; k < MAXPOTION; k++) + { + + lprc (potionname[k][0]); + } + + lwrite ((char *) spelknow, SPNUM * sizeof (int)); + lprint (wizard); + lprint (rmst); /* random monster generation counter */ + + for (i = 0; i < 90; i++) + { + + lprint (dnd_item[i].qty); + } + + lwrite ((char *) course, 25 * sizeof (int)); + lprint (cheat); + + for (i = 0; i < MAXMONST; i++) + lprint (monster[i].genocided); /* genocide info */ + + for (sp = spheres; sp; sp = sp->p) + lwrite ((char *) sp, sizeof (struct sphere)); /* save spheres of annihilation */ + + time (&zzz); + temptime = zzz - initialtime; + lwrite ((char *) &temptime, sizeof (time_t)); + /* lwrite( (char*)&zzz, sizeof(time_t) ); */ + + lprint (VERSION); + lprint (SUBVERSION); + + lwclose (); + + lastmonst[0] = 0; + setscroll (); + lcreat ((char *) 0); + return (0); +} + + +void +restoregame (char *fname) +{ + int i, k; + struct sphere *sp, *sp2; + /*struct stat filetimes; */ + time_t temptime; + + cursors (); + lprcat ("\nRestoring . . ."); + lflush (); + if (lopen (fname) <= 0) + { + lcreat ((char *) 0); + lprintf ("\nCan't open file <%s>to restore game\n", fname); + nap (NAPTIME); + cdesc[GOLD] = cdesc[BANKACCOUNT] = 0; + died (-265); + return; + } + + lrfill (logname, LOGNAMESIZE); + + lrfill ((char *) beenhere, (sizeof (int) * (MAXLEVEL + MAXVLEVEL))); + + for (k = 0; k < MAXLEVEL + MAXVLEVEL; k++) + { + if (!beenhere[k]) + continue; + lrfill ((char *) &cell[(int) k * MAXX * MAXY], + sizeof (struct cel) * MAXY * MAXX); + } + + lrfill ((char *) &cdesc[0], 100 * sizeof (long)); + + lrfill ((char *) >ime, 1 * sizeof (long)); + level = cdesc[CAVELEVEL] = larint (); + playerx = larint (); + playery = larint (); + + lrfill ((char *) iven, 26 * sizeof (int)); + lrfill ((char *) ivenarg, 26 * sizeof (int)); + + for (k = 0; k < MAXSCROLL; k++) + { + + scrollname[k][0] = lgetc (); + } + + for (k = 0; k < MAXPOTION; k++) + { + + potionname[k][0] = lgetc (); + } + + lrfill ((char *) spelknow, SPNUM * sizeof (int)); + wizard = larint (); + rmst = larint (); /* random monster creation flag */ + + for (i = 0; i < 90; i++) + { + + dnd_item[i].qty = larint (); + } + + lrfill ((char *) course, 25 * sizeof (int)); + cheat = larint (); + + + for (i = 0; i < MAXMONST; i++) + monster[i].genocided = larint (); /* genocide info */ + + for (sp = 0, i = 0; i < cdesc[SPHCAST]; i++) + { + sp2 = sp; + sp = (struct sphere *) malloc (sizeof (struct sphere)); + if (sp == 0) + { + fprintf (stderr, "Can't malloc() for sphere space\n"); + break; + } + lrfill ((char *) sp, sizeof (struct sphere)); /* get spheres of annihilation */ + sp->p = 0; /* null out pointer */ + if (i == 0) + spheres = sp; /* beginning of list */ + else + sp2->p = sp; + } + + time (&zzz); + lrfill ((char *) &temptime, sizeof (time_t)); + initialtime = zzz - temptime; + + /* I don't care if ppl feel the need to cheat - edwin */ + /* get the creation and modification time of file */ + /* fstat(fd,&filetimes); + lrfill( (char*)&zzz, sizeof(time_t) ); + zzz += 6; + if (filetimes.st_ctime > zzz) { + fsorry(); + } else if (filetimes.st_mtime > zzz) { + fsorry(); + } */ + + /* version check */ + if (VERSION != larint () || SUBVERSION != larint ()) + { + lrclose (); + cheat = 1; + cursor (1, 23); + lprcat ("Sorry, But your save file is for an older version of larn\n"); + lflush (); + nap (NAPTIME); + cdesc[GOLD] = cdesc[BANKACCOUNT] = 0; + died (-266); + return; + } + + lrclose (); + + oldx = oldy = 0; + + if (cdesc[HP] < 0) + { + died (284); + return; + } /* died a post mortem death */ + +#ifdef ANTICHEAT + if (_unlink (fname) < 0) + fcheat (); + for (k = 0; k < 6; k++) + if (cdesc[k] > 99) + greedy (); + if (cdesc[HPMAX] > 999 || cdesc[SPELLMAX] > 125) + greedy (); +#endif + + /* if patch up lev 25 player */ + if (cdesc[LEVEL] == 25 && cdesc[EXPERIENCE] > skill[24]) + { + long tmp; + tmp = cdesc[EXPERIENCE] - skill[24]; /* amount to go up */ + cdesc[EXPERIENCE] = skill[24]; + raiseexperience (tmp); + } + getlevel (); + gtime -= 1; /* HACK for time advancing either on save or reload */ + lasttime = gtime - 1; +} + +#ifdef ANTICHEAT + +/* +* subroutine to not allow greedy cheaters +*/ +static void +greedy (void) +{ + +#if WIZID + if (wizard) + { + + return; + } +#endif + + lprcat + ("\n\nI am so sorry, but your character is a little TOO good! Since this\n"); + lprcat + ("cannot normally happen from an honest game, I must assume that you cheated.\n"); + lprcat + ("In that you are GREEDY as well as a CHEATER, I cannot allow this game\n"); + lprcat ("to continue.\n"); + + nap (5000); + cdesc[GOLD] = cdesc[BANKACCOUNT] = 0; + died (-267); +} + + +/* +* subroutine to not allow altered save files and terminate the attempted +* restart +*/ +static void +fsorry (void) +{ + + lprcat ("\nSorry, but your savefile has been altered.\n"); + lprcat ("However, seeing as I am a good sport, I will let you play.\n"); + lprcat ("Be advised though, you won't be placed on the normal scoreboard."); + + cheat = 1; + nap (4000); +} + + +/* +* subroutine to not allow game if save file can't be deleted +*/ +static void +fcheat (void) +{ + +#if WIZID + if (wizard) + { + + return; + } +#endif + + lprcat + ("\nSorry, but your savefile can't be deleted. This can only mean\n"); + lprcat + ("that you tried to CHEAT by protecting the directory the savefile\n"); + lprcat + ("is in. Since this is unfair to the rest of the larn community, I\n"); + lprcat ("cannot let you play this game.\n"); + + nap (5000); + cdesc[GOLD] = cdesc[BANKACCOUNT] = 0; + died (-268); +} + +#endif diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..a4c3f2c --- /dev/null +++ b/src/display.c @@ -0,0 +1,973 @@ +#include +#include +#include +#include "includes/action.h" +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/ansiterm.h" +#include "includes/create.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/io.h" +#include "includes/monster.h" + +#define botsub( _idx, _x, _y, _str ) \ + if ( cdesc[(_idx)] != cbak[(_idx)] ) \ + { \ + cbak[(_idx)] = cdesc[(_idx)]; \ + cursor( (_x), (_y) ); \ + lprintf( (_str), (int)cdesc[(_idx)] ); \ + } + +#define nlprc(_ch) lprc(_ch) + +static void bot_hpx (void); +static void bot_spellx (void); +static void botside (void); +static void seepage (void); +static int minx, maxx, miny, maxy; +static int bot1f = 0, bot2f = 0, bot3f = 0; +static int always = 0; +int regen_bottom = 0; + +/* +bottomline() + +now for the bottom line of the display +*/ +void +bottomline (void) +{ + + recalc (); + bot1f = 1; +} + + +void +bottomhp (void) +{ + + bot2f = 1; +} + + +void +bottomspell (void) +{ + + bot3f = 1; +} + +void +bottomdo (void) +{ + if (bot1f) + { + bot3f = bot1f = bot2f = 0; + bot_linex (); + return; + } + if (bot2f) + { + bot2f = 0; + bot_hpx (); + } + if (bot3f) + { + bot3f = 0; + bot_spellx (); + } +} + + +void +bot_linex (void) +{ + int i; + /*int debugtmp; */ + + if (regen_bottom || (always)) + { + regen_bottom = FALSE; + cursor (1, 18); + if (cdesc[SPELLMAX] > 99) { + attron(COLOR_PAIR(3)); + lprintf ("Spells:"); + attroff(COLOR_PAIR(3)); + lprintf("%3d(%3d)", (int) cdesc[SPELLS], + (int) cdesc[SPELLMAX]); + } else { + attron(COLOR_PAIR(1)); + lprintf ("Spells:"); + attroff(COLOR_PAIR(1)); + lprintf("%3d(%2d) ", (int) cdesc[SPELLS], + (int) cdesc[SPELLMAX]); + } + attron(COLOR_PAIR(1)); + lprintf(" AC:"); + attroff(COLOR_PAIR(1)); + lprintf(" %-3d ",(int) cdesc[AC]); + attron(COLOR_PAIR(1)); + lprintf(" WC:"); + attroff(COLOR_PAIR(1)); + lprintf(" %-3d ",(int) cdesc[WCLASS]); + attron(COLOR_PAIR(1)); + lprintf("Level:"); + attroff(COLOR_PAIR(1)); + if (cdesc[LEVEL] > 99) { + lprintf ("%3d", (int) cdesc[LEVEL]); + } else { + lprintf (" %-2d", (int) cdesc[LEVEL]); + } + /*debugtmp = cdesc[LEVEL]; */ + attron(COLOR_PAIR(1)); + lprintf (" Exp:"); + attroff(COLOR_PAIR(1)); + lprintf(" %-9d %s\n", (int) cdesc[EXPERIENCE], + classname[cdesc[LEVEL] - 1]); + /*This is sill here to initially show the health stats. ~Gibbon*/ + attron(COLOR_PAIR(1)); + lprintf("HP:"); + attroff(COLOR_PAIR(1)); + lprintf(" %3d(%3d)", (int)cdesc[HP], (int)cdesc[HPMAX]); + attron(COLOR_PAIR(1)); + lprintf(" STR:"); + attroff(COLOR_PAIR(1)); + lprintf("%-2d ",(int) (cdesc[STRENGTH] + cdesc[STREXTRA])); + attron(COLOR_PAIR(1)); + lprintf("INT:"); + attroff(COLOR_PAIR(1)); + lprintf("%-2d ",(int) cdesc[INTELLIGENCE]); + attron(COLOR_PAIR(1)); + lprintf ("WIS:"); + attroff(COLOR_PAIR(1)); + lprintf("%-2d ",(int) cdesc[WISDOM]); + attron(COLOR_PAIR(1)); + lprintf("CON:"); + attroff(COLOR_PAIR(1)); + lprintf("%-2d ",(int) cdesc[CONSTITUTION]); + attron(COLOR_PAIR(1)); + lprintf("DEX:"); + attroff(COLOR_PAIR(1)); + lprintf("%-2d ",(int) cdesc[DEXTERITY]); + attron(COLOR_PAIR(1)); + lprintf("CHA:"); + attroff(COLOR_PAIR(1)); + lprintf("%-2d ",(int) cdesc[CHARISMA]); + attron(COLOR_PAIR(1)); + lprintf("LV:"); + attroff(COLOR_PAIR(1)); + + if ((level == 0) || (wizard)) + cdesc[TELEFLAG] = 0; + if (cdesc[TELEFLAG]) + lprcat (" ?"); + else + lprcat (levelname[level]); + attron(COLOR_PAIR(1)); + lprintf (" Gold: "); + attroff(COLOR_PAIR(1)); + lprintf("%-6d", (int) cdesc[GOLD]); + always = 1; + botside (); + cdesc[TMP] = cdesc[STRENGTH] + cdesc[STREXTRA]; + for (i = 0; i < 100; i++) + cbak[i] = cdesc[i]; + return; + } + + botsub (SPELLS, 8, 18, "%3d"); + if (cdesc[SPELLMAX] > 99) + { + botsub (SPELLMAX, 12, 18, "%3d)"); + } + else + botsub (SPELLMAX, 12, 18, "%2d) "); + botsub (HP, 5, 19, "%3d"); + botsub (HPMAX, 9, 19, "%3d"); + botsub (AC, 21, 18, "%-3d"); + botsub (WCLASS, 30, 18, "%-3d"); + botsub (EXPERIENCE, 49, 18, "%-9d"); + if (cdesc[LEVEL] != cbak[LEVEL]) + { + cursor (59, 18); + lprcat (classname[cdesc[LEVEL] - 1]); + } + if (cdesc[LEVEL] > 99) + { + botsub (LEVEL, 40, 18, "%3d"); + } + else + botsub (LEVEL, 40, 18, " %-2d"); + cdesc[TMP] = cdesc[STRENGTH] + cdesc[STREXTRA]; + botsub (TMP, 18, 19, "%-2d"); + botsub (INTELLIGENCE, 25, 19, "%-2d"); + botsub (WISDOM, 32, 19, "%-2d"); + botsub (CONSTITUTION, 39, 19, "%-2d"); + botsub (DEXTERITY, 46, 19, "%-2d"); + botsub (CHARISMA, 53, 19, "%-2d"); + if ((level != cbak[CAVELEVEL]) || (cdesc[TELEFLAG] != cbak[TELEFLAG])) + { + if ((level == 0) || (wizard)) + cdesc[TELEFLAG] = 0; + cbak[TELEFLAG] = cdesc[TELEFLAG]; + cbak[CAVELEVEL] = level; + cursor (59, 19); + if (cdesc[TELEFLAG]) + lprcat (" ?"); + else + lprcat (levelname[level]); + } + botsub (GOLD, 69, 19, "%-6d"); + botside (); +} + + + +/* +special subroutine to update only the gold number on the bottomlines +called from ogold() +*/ +void +bottomgold (void) +{ + + botsub (GOLD, 69, 19, "%-6d"); +} + + + +/* +special routine to update hp and level fields on bottom lines +called in monster.c hitplayer() and spattack() +*/ +static void +bot_hpx (void) +{ + /*This is added to update the color when hit. + *It's redrawing over the top (1,19) ~Gibbon + */ + if (cdesc[HP] < 10) { + cursor(1, 19); + attron(COLOR_PAIR(1)); + lprintf("HP:"); + attroff(COLOR_PAIR(1)); + attron(COLOR_PAIR(2)); + lprintf(" %3d", (int)cdesc[HP]); + attroff(COLOR_PAIR(2)); + } + else { + cursor(1, 19); + attron(COLOR_PAIR(1)); + lprintf("HP:"); + attroff(COLOR_PAIR(1)); + lprintf(" %3d", (int)cdesc[HP]); + } + if (cdesc[EXPERIENCE] != cbak[EXPERIENCE]) + { + recalc (); + bot_linex (); + } + else + botsub (HP, 5, 19, "%3d"); +} + + + +/* +special routine to update number of spells called from regen() +*/ +static void +bot_spellx (void) +{ + + botsub (SPELLS, 9, 18, "%2d"); +} + + + +/* +common subroutine for a more economical bottomline() +*/ +struct bot_side_def +{ + + int typ; + char *string; + +}; + + +static struct bot_side_def bot_data[] = { + + {STEALTH, "stealth"}, + {UNDEADPRO, "undead pro"}, + {SPIRITPRO, "spirit pro"}, + {CHARMCOUNT, "Charm"}, + {TIMESTOP, "Time Stop"}, + {HOLDMONST, "Hold Monst"}, + {GIANTSTR, "Giant Str"}, + {FIRERESISTANCE, "Fire Resit"}, + {DEXCOUNT, "Dexterity"}, + {STRCOUNT, "Strength"}, + {SCAREMONST, "Scare"}, + {HASTESELF, "Haste Self"}, + {CANCELLATION, "Cancel"}, + {INVISIBILITY, "Invisible"}, + {ALTPRO, "Protect 3"}, + {PROTECTIONTIME, "Protect 2"}, + {WTW, "Wall-Walk"} + +}; + + +static void +botside (void) +{ + int i, idx; + for (i = 0; i < 17; i++) + { + idx = bot_data[i].typ; + if ((always) || (cdesc[idx] != cbak[idx])) + { + if ((always) || (cbak[idx] == 0)) + { + if (cdesc[idx]) + { + cursor (70, i + 1); + lprcat (bot_data[i].string); + + /*Reset cursor position. ~Gibbon */ + cursors(); + } + } + else if (cdesc[idx] == 0) + { + cursor (70, i + 1); + lprcat (" "); + + /*Reset cursor position. ~Gibbon */ + cursors(); + } + cbak[idx] = cdesc[idx]; + } + } + always = 0; +} + +/* +* subroutine to draw only a section of the screen +* only the top section of the screen is updated. If entire lines are being +* drawn, then they will be cleared first. +*/ +static int d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY; /* for limited screen drawing */ + +void +draws (int xmin, int xmax, int ymin, int ymax) +{ + int i, idx; + + if (xmin == 0 && xmax == MAXX) /* clear section of screen as needed */ + { + if (ymin == 0) + cl_up (79, ymax); + else + for (i = ymin; i < ymin; i++) + cl_line (1, i + 1); + xmin = -1; + } + d_xmin = xmin; + d_xmax = xmax; + d_ymin = ymin; + d_ymax = ymax; /* for limited screen drawing */ + drawscreen (); + if (xmin <= 0 && xmax == MAXX) /* draw stuff on right side of screen as needed */ + { + for (i = ymin; i < ymax; i++) + { + idx = bot_data[i].typ; + if (cdesc[idx]) + { + cursor (70, i + 1); + lprcat (bot_data[i].string); + } + cbak[idx] = cdesc[idx]; + } + } +} + + + +/* +drawscreen() + +subroutine to redraw the whole screen as the player knows it +*/ +static int d_flag; + +void +drawscreen (void) +{ + int i, j, k, ileft, iright; + + if (d_xmin == 0 && d_xmax == MAXX && d_ymin == 0 && d_ymax == MAXY) + { + + /* clear the screen */ + d_flag = 1; + screen_clear(); + + } + else + { + + d_flag = 0; + cursor (1, 1); + } + + if (d_xmin < 0) + { + + /* d_xmin=-1 means display all without bottomline */ + d_xmin = 0; + } + + /* display lines of the screen */ + for (j = d_ymin; j < d_ymax; j++) + { + + /* When we show a spot of the dungeon, we have 4 cases: + squares we know nothing about + - know == 0 + squares we've been at and still know whats there + - know == KNOWALL (== KNOWHERE | HAVESEEN) + squares we've been at, but don't still recall because + something else happened there. + - know == HAVESEEN + squares we recall, but haven't been at (an error condition) + - know == KNOWHERE + + to minimize printing of spaces, scan from left of line until + we reach a location that the user knows. + */ + + ileft = d_xmin - 1; + + while (++ileft < d_xmax) + { + + if (know[ileft][j]) + { + + break; + } + } + + /* if blank line ... */ + if (ileft >= d_xmax) + { + + continue; + } + + + + /* scan from right of line until we reach a location that the + user knows. + */ + iright = d_xmax; + + while (--iright > ileft) + { + + if (know[iright][j]) + { + + break; + } + } + + /* + * now print the line, after positioning the cursor. + * print the line with bold objects in a different + * loop for effeciency + */ + cursor (ileft + 1, j + 1); + + for (i = ileft; i <= iright; i++) + { + + /* we still need to check for the location being known, + for we might have an unknown spot in the middle of + an otherwise known line. + */ + if (know[i][j] == 0) + { + + nlprc (' '); + + } + else if (know[i][j] & HAVESEEN) + { + + /* + * if monster there and the user still knows the place, + * then show the monster. Otherwise, show what was + * there before. + */ + + if (i == playerx && j == playery) + { + nlprc ('@'); + continue; + } + + k = mitem[i][j]; + + if (k && know[i][j] & KNOWHERE) + { + + nlprc (monstnamelist[k]); + + } + else + { + + nlprc (objnamelist[item[i][j]]); + } + + } + else + { + + /* + * error condition. recover by resetting location + * to an 'unknown' state. + */ + + nlprc (' '); + + mitem[i][j] = item[i][j] = 0; + } + } + } + + resetbold (); + + if (d_flag) + { + + always = 1; + botside (); + always = 1; + bot_linex (); + } + + /* oldx=99; */ + /* for limited screen drawing */ + d_xmin = d_ymin = 0; + d_xmax = MAXX; + d_ymax = MAXY; +} + + + + +/* +showcell(x,y) + +subroutine to display a cell location on the screen +*/ +void +showcell (int x, int y) +{ + int i, j, k, m; + + if (cdesc[BLINDCOUNT]) + return; /* see nothing if blind */ + if (cdesc[AWARENESS]) + { + minx = x - 3; + maxx = x + 3; + miny = y - 3; + maxy = y + 3; + } + else if (iven[cdesc[WIELD]] == OHSWORD + && ivenarg[cdesc[WIELD]] >=0) + { + minx = x - 2; + maxx = x + 2; + miny = y - 2; + maxy = y + 2; + } + else + { + minx = x - 1; + maxx = x + 1; + miny = y - 1; + maxy = y + 1; + } + + if (minx < 0) + minx = 0; + if (maxx > MAXX - 1) + maxx = MAXX - 1; + if (miny < 0) + miny = 0; + if (maxy > MAXY - 1) + maxy = MAXY - 1; + + for (j = miny; j <= maxy; j++) + for (m = minx; m <= maxx; m++) + if ((know[m][j] & KNOWHERE) == 0) + { + cursor (m + 1, j + 1); + x = maxx; + while (know[x][j] & KNOWHERE) + --x; + for (i = m; i <= x; i++) + { + if ((k = mitem[i][j]) != 0) + lprc (monstnamelist[k]); + else + switch (k = item[i][j]) + { + case OWALL: + case 0: + case OIVTELETRAP: + case OTRAPARROWIV: + case OIVDARTRAP: + case OIVTRAPDOOR: + lprc (objnamelist[k]); + break; + default: + lprc (objnamelist[k]); + break; + }; + know[i][j] = KNOWALL; + } + m = maxx; + + } +} + + + +/* +this routine shows only the spot that is given it. the spaces around +these coordinated are not shown +used in godirect() in monster.c for missile weapons display +*/ +void +show1cell (int x, int y) +{ + int k; + + cursor (x + 1, y + 1); + + /* see nothing if blind, but clear previous player position */ + if (cdesc[BLINDCOUNT]) + { + + if (x == oldx && y == oldy) + lprc (' '); + + return; + } + + k = mitem[x][y]; + + if (k) + { + + lprc (monstnamelist[k]); + + } + else + { + + k = item[x][y]; + + switch (k) + { + + case OWALL: + case 0: + case OIVTELETRAP: + case OTRAPARROWIV: + case OIVDARTRAP: + case OIVTRAPDOOR: + lprc (objnamelist[k]); + break; + default: + lprc (objnamelist[k]); + } + } + + + /* we end up knowing about it */ + know[x][y] = KNOWALL; +} + + + + +/* +showplayer() + +subroutine to show where the player is on the screen +cursor values start from 1 up +*/ +void +showplayer (void) +{ + show1cell (oldx, oldy); + cursor (playerx + 1, playery + 1); + oldx = playerx; + oldy = playery; + refresh(); +} + + + + + + +/* +moveplayer(dir) + +subroutine to move the player from one room to another +returns 0 if can't move in that direction or hit a monster or on an object +else returns 1 +nomove is set to 1 to stop the next move (inadvertent monsters hitting +players when walking into walls) if player walks off screen or into wall +*/ +int diroffx[] = { 0, 0, 1, 0, -1, 1, -1, 1, -1 }; +int diroffy[] = { 0, 1, 0, -1, 0, -1, -1, 1, 1 }; + +int +moveplayer (int dir) +/* from = present room # direction = [1-north] +[2-east] [3-south] [4-west] [5-northeast] +[6-northwest] [7-southeast] [8-southwest] +if direction=0, don't move--just show where he is */ +{ + int k, m, i, j; + + if (cdesc[CONFUSE]) + if (cdesc[LEVEL] < rnd (30)) + dir = rund (9); /*if confused any dir */ + k = playerx + diroffx[dir]; + m = playery + diroffy[dir]; + if (k < 0 || k >= MAXX || m < 0 || m >= MAXY) + { + nomove = 1; + return (yrepcount = 0); + } + i = item[k][m]; + j = mitem[k][m]; + + /* prevent the player from moving onto a wall, or a closed door, + unless the character has Walk-Through-Walls. + */ + if ((i == OCLOSEDDOOR || i == OWALL) && cdesc[WTW] == 0) + { + nomove = 1; + return (yrepcount = 0); + } + if (k == 33 && m == MAXY - 1 && level == 1) + { + newcavelevel (0); + for (k = 0; k < MAXX; k++) + for (m = 0; m < MAXY; m++) + if (item[k][m] == OENTRANCE) + { + playerx = k; + playery = m; + positionplayer (); + drawscreen (); + return (0); + } + } + /* hit a monster + */ + if (j > 0) + { + hitmonster (k, m); + return (yrepcount = 0); + } + + /* check for the player ignoring an altar + */ + if (item[playerx][playery] == OALTAR && !prayed) + { + cursors (); + lprcat ("\nYou have ignored the altar!"); + act_ignore_altar (); + } + prayed = 0; + + lastpx = playerx; + lastpy = playery; + playerx = k; + playery = m; + if (i && i != OTRAPARROWIV && i != OIVTELETRAP && i != OIVDARTRAP + && i != OIVTRAPDOOR) + return (yrepcount = 0); + else + return (1); +} + + + + +/* +* function to show what magic items have been discovered thus far +* enter with -1 for just spells, anything else will give scrolls & potions +*/ +static int lincount, count; + +void +seemagic (int arg) +{ + int i, j, k, number; + int sort[SPNUM + 1]; /* OK as long as SPNUM > MAXSCROLL,MAXPOTION */ + + count = lincount = 0; + + /* count and sort the known spell codes + */ + for (j = 0; j <= SPNUM; j++) + sort[j] = SPNUM; + for (number = i = 0; i < SPNUM; i++) + if (spelknow[i]) + { + number++; + j = 0; + while (strncmp (spelcode[sort[j]], spelcode[i], 3) < 0) + j++; + k = number - 1; + while (k > j) + sort[k] = sort[k - 1], k--; + sort[j] = i; + } + + if (arg == -1) /* if display spells while casting one */ + { + cl_up (79, ((number + 2) / 3 + 4)); /* lines needed for display */ + cursor (1, 1); + } + else + { + resetscroll (); + screen_clear(); + } + + lprcat ("The magic spells you have discovered thus far:\n\n"); + for (i = 0; i < number; i++) + { + lprintf ("%s %-20s ", spelcode[sort[i]], spelname[sort[i]]); + seepage (); + } + + if (arg == -1) + { + seepage (); + more (FALSE); + draws (0, MAXX, 0, ((number + 2) / 3 + 4)); + return; + } + + lincount += 3; + if (count != 0) + { + count = 2; + seepage (); + } + + /* count and sort the known scrolls + */ + for (j = 0; j <= MAXSCROLL; j++) + sort[j] = MAXSCROLL; + for (number = i = 0; i < MAXSCROLL; i++) + if (scrollname[i][0]) + { + number++; + j = 0; + while (strcmp (&scrollname[sort[j]][1], &scrollname[i][1]) < 0) + j++; + k = number - 1; + while (k > j) + sort[k] = sort[k - 1], k--; + sort[j] = i; + } + + lprcat ("\nThe magic scrolls you have found to date are:\n\n"); + count = 0; + for (i = 0; i < number; i++) + { + lprintf ("%-26s", &scrollname[sort[i]][1]); + seepage (); + } + + lincount += 3; + if (count != 0) + { + count = 2; + seepage (); + } + + /* count and sort the known potions + */ + for (j = 0; j <= MAXPOTION; j++) + sort[j] = MAXPOTION; + for (number = i = 0; i < MAXPOTION; i++) + if (potionname[i][0]) + { + number++; + j = 0; + while (strcmp (&potionname[sort[j]][1], &potionname[i][1]) < 0) + j++; + k = number - 1; + while (k > j) + sort[k] = sort[k - 1], k--; + sort[j] = i; + } + + lprcat ("\nThe magic potions you have found to date are:\n\n"); + count = 0; + for (i = 0; i < number; i++) + { + lprintf ("%-26s", &potionname[sort[i]][1]); + seepage (); + } + + if (lincount != 0) + more (FALSE); + setscroll (); + drawscreen (); +} + + + +/* +* subroutine to paginate the seemagic function +*/ +static void +seepage (void) +{ + if (++count == 3) + { + lincount++; + count = 0; + lprc ('\n'); + if (lincount > 17) + { + lincount = 0; + more (FALSE); + screen_clear(); + } + } +} diff --git a/src/fortune.c b/src/fortune.c new file mode 100644 index 0000000..fc21cdd --- /dev/null +++ b/src/fortune.c @@ -0,0 +1,115 @@ +/* fortune.c */ +#include +#include + +#include +#include +#if defined NIX +#include +#endif + +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/fortune.h" +#include "includes/global.h" +#include "includes/io.h" + +static void msdosfortune (void); +static int fortsize = 0; + +void +outfortune (void) +{ + + lprcat ("\nThe cookie was delicious."); + + if (cdesc[BLINDCOUNT]) + { + + return; + } + + msdosfortune (); +} + + + +/* +* Rumors has been entirely rewritten to be disk based. This is marginally +* slower, but requires no mallocked memory. Notice this in only valid for +* files smaller than 32K. +*/ +static void +msdosfortune (void) +{ + int status, i; + FILE *fp; + char buf[BUFSIZ], ch; + + /* We couldn't open fortunes */ + if (fortsize < 0) + { + return; + } + + fp = fopen (fortfile, "r"); + + if (fp == 0) + { + + /* Don't try opening it again */ + fortsize = -1; + + return; + } + + if (fortsize == 0) + { + fseek (fp, 0L, SEEK_END); + fortsize = ftell (fp); + } + + if (fseek (fp, (long) rund (fortsize), SEEK_SET) < 0) + { + return; + } + + /* + * Skip to next newline or EOF + */ + do + { + status = fread (&ch, sizeof (char), 1, fp); + } + while (status != EOF && ch != '\n'); + + if (status == EOF) + { + /* back to the beginning */ + if (fseek (fp, 0L, SEEK_SET) < 0) + { + return; + } + } + + /* + * Read in the line. + */ + for (i = 0; i < BUFSIZ - 1; i++) + { + if (fread (&buf[i], sizeof (char), 1, fp) != 1 || buf[i] == '\n') + { + break; + } + } + buf[i] = '\0'; + + /* + * And spit it out + */ + lprcat (" Inside you find a scrap of paper that says:\n"); + lprcat (buf); + + fclose (fp); +} diff --git a/src/global.c b/src/global.c new file mode 100644 index 0000000..6108a65 --- /dev/null +++ b/src/global.c @@ -0,0 +1,869 @@ +/* global.c +* +* raiselevel() subroutine to raise the player one level +* loselevel() subroutine to lower the player by one level +* raiseexperience(x) subroutine to increase experience points +* loseexperience(x) subroutine to lose experience points +* losehp(x) subroutine to remove hit points from the player +* losemhp(x) subroutine to remove max # hit points from the player +* raisehp(x) subroutine to gain hit points +* raisemhp(x) subroutine to gain maximum hit points +* losemspells(x) subroutine to lose maximum spells +* raisemspells(x) subroutine to gain maximum spells +* makemonst(lev) function to return monster number for a randomly selected monster +* positionplayer() function to be sure player is not in a wall +* recalc() function to recalculate the armor class of the player +* quit() subroutine to ask if the player really wants to quit +* more() +* take() +* drop_object() +* enchantarmor() +* enchweapon() +* pocketfull() +* nearbymonst() +* stealsomething() +* emptyhanded() +* creategem() +* adjustcvalues() +* gettokstr() +* getpassword() +* getyn() +* packweight() +*/ + +#include +#include +#include + +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/inventory.h" +#include "includes/io.h" +#include "includes/monster.h" +#include "includes/scores.h" +#include "includes/sysdep.h" + +/* +* raiselevel() +* +* subroutine to raise the player one level +* uses the skill[] array to find level boundarys +* uses cdesc[EXPERIENCE] cdesc[LEVEL] +*/ +void +raiselevel (void) +{ + + if (cdesc[LEVEL] < MAXPLEVEL) + raiseexperience ((skill[cdesc[LEVEL]] - cdesc[EXPERIENCE])); +} + + + +/* +* loselevel() +* +* subroutine to lower the players character level by one +*/ +void +loselevel (void) +{ + if (cdesc[LEVEL] > 1) + loseexperience ((cdesc[EXPERIENCE] - skill[cdesc[LEVEL] - 1] + 1)); +} + + + +/* +* raiseexperience(x) +* +* subroutine to increase experience points +*/ +void +raiseexperience (long x) +{ + int i, tmp; + + i = cdesc[LEVEL]; + cdesc[EXPERIENCE] += x; + while (cdesc[EXPERIENCE] >= skill[cdesc[LEVEL]] + && (cdesc[LEVEL] < MAXPLEVEL)) + { + tmp = (cdesc[CONSTITUTION] - cdesc[HARDGAME]) >> 1; + cdesc[LEVEL]++; + raisemhp ((int) (rnd (3) + rnd ((tmp > 0) ? tmp : 1))); + raisemspells ((int) rund (3)); + if (cdesc[LEVEL] < 7 - cdesc[HARDGAME]) + raisemhp ((int) (cdesc[CONSTITUTION] >> 2)); + } + if (cdesc[LEVEL] != i) + { + cursors (); + lprintf ("\nWelcome to level %d", (int) cdesc[LEVEL]); /* if we changed levels */ + } + bottomline (); +} + + + +/* +* loseexperience(x) +* +* subroutine to lose experience points +*/ +void +loseexperience (long x) +{ + int i, tmp; + + i = cdesc[LEVEL]; + cdesc[EXPERIENCE] -= x; + if (cdesc[EXPERIENCE] < 0) + cdesc[EXPERIENCE] = 0; + while (cdesc[EXPERIENCE] < skill[cdesc[LEVEL] - 1]) + { + if (--cdesc[LEVEL] <= 1) + cdesc[LEVEL] = 1; /* down one level */ + tmp = (cdesc[CONSTITUTION] - cdesc[HARDGAME]) >> 1; /* lose hpoints */ + losemhp ((int) rnd ((tmp > 0) ? tmp : 1)); /* lose hpoints */ + if (cdesc[LEVEL] < 7 - cdesc[HARDGAME]) + losemhp ((int) (cdesc[CONSTITUTION] >> 2)); + losemspells ((int) rund (3)); /* lose spells */ + } + if (i != cdesc[LEVEL]) + { + cursors (); + lprintf ("\nYou went down to level %d!", (int) cdesc[LEVEL]); + } + bottomline (); +} + + + +/* +* losehp(x) +* losemhp(x) +* +* subroutine to remove hit points from the player +* warning -- will kill player if hp goes to zero +*/ +void +losehp (int x) +{ + if ((cdesc[HP] -= x) <= 0) + { + lprcat ("\nYou have been slain."); + nap (NAPTIME); + died (lastnum); + } +} + +void +losemhp (int x) +{ + cdesc[HP] -= x; + if (cdesc[HP] < 1) + cdesc[HP] = 1; + cdesc[HPMAX] -= x; + if (cdesc[HPMAX] < 1) + cdesc[HPMAX] = 1; +} + + + +/* +* raisehp(x) +* raisemhp(x) +* +* subroutine to gain maximum hit points +*/ +void +raisehp (int x) +{ + + if ((cdesc[HP] += x) > cdesc[HPMAX]) + { + + cdesc[HP] = cdesc[HPMAX]; + } +} + +void +raisemhp (int x) +{ + + cdesc[HPMAX] += x; + cdesc[HP] += x; +} + + +/* +* raisemspells(x) +* +* subroutine to gain maximum spells +*/ +void +raisemspells (int x) +{ + + cdesc[SPELLMAX] += x; + cdesc[SPELLS] += x; +} + + +/* +* losemspells(x) +* +* subroutine to lose maximum spells +*/ +void +losemspells (int x) +{ + + if ((cdesc[SPELLMAX] -= x) < 0) + cdesc[SPELLMAX] = 0; + if ((cdesc[SPELLS] -= x) < 0) + cdesc[SPELLS] = 0; +} + + + +/* + * makemonst(lev) + * int lev; + * + * function to return monster number for a randomly selected monster + * for the given cave level + * + * FIXED by ~Gibbon. Randomness was removed prior to 12.4alpha2 for some reason, so + * monsters were being taken based on the int of monstlevel (5,11 etc). + * + * Basically unchanged from 12.3 but cleaned up. Don't fix what isn't broken. + */ +int +makemonst (int lev) +{ + int x, tmp; + if (lev < 1) + { + lev = 1; + } + else if (lev > 12) + { + lev = 12; + } + if (lev < 5) + { + tmp = rnd ((x = monstlevel[lev - 1]) ? x : 1); + } + else + { + tmp = + rnd ((x = + monstlevel[lev - 1] - monstlevel[lev - 4]) ? x : 1) + + monstlevel[lev - 4]; + } + while (monster[tmp].genocided && tmp < MAXMONST) + { + tmp++; /* genocided? */ + } + return tmp; +} + +/* +* positionplayer() +* +* Insure player is not in a wall or on top of a monster. Could be more +* intelligent about what kinds of objects the player can land on. +*/ +void +positionplayer (void) +{ + int z, try = 2; + + /* set the previous player x,y position to the new one, so that + clearing the player indicator from the previous location will + not do the wrong thing. + */ + oldx = playerx; + oldy = playery; + + /* short-circuit the testing if current position empty + */ + if (!item[playerx][playery] && !mitem[playerx][playery]) + return; + + /* make at most two complete passes across the dungeon, looking + for a clear space. In most situations, should find a clear + spot right around the current player position. + */ + do + { + + /* check all around the player position for a clear space. + */ + for (z = 1; z < 9; z++) + { + int tmpx = playerx + diroffx[z]; + int tmpy = playery + diroffy[z]; + if (!item[tmpx][tmpy] && !mitem[tmpx][tmpy]) + { + playerx = tmpx; + playery = tmpy; + return; + } + } + + /* no clear spots around the player. try another position, + wrapping around the dungeon. + */ + if (++playerx >= MAXX - 1) + { + playerx = 1; + if (++playery >= MAXY - 1) + { + playery = 1; + try--; + } + } + } + while (try); + + /* no spot found. + */ + lprcat ("Failure in positionplayer\n"); +} + + + +/* +* recalc() function to recalculate the armor class of the player +*/ +void +recalc (void) +{ + int i, j, k; + + cdesc[AC] = cdesc[MOREDEFENSES]; + if (cdesc[WEAR] >= 0) + switch (iven[cdesc[WEAR]]) + { + case OSHIELD: + cdesc[AC] += 2 + ivenarg[cdesc[WEAR]]; + break; + case OLEATHER: + cdesc[AC] += 2 + ivenarg[cdesc[WEAR]]; + break; + case OSTUDLEATHER: + cdesc[AC] += 3 + ivenarg[cdesc[WEAR]]; + break; + case ORING: + cdesc[AC] += 5 + ivenarg[cdesc[WEAR]]; + break; + case OCHAIN: + cdesc[AC] += 6 + ivenarg[cdesc[WEAR]]; + break; + case OSPLINT: + cdesc[AC] += 7 + ivenarg[cdesc[WEAR]]; + break; + case OPLATE: + cdesc[AC] += 9 + ivenarg[cdesc[WEAR]]; + break; + case OPLATEARMOR: + cdesc[AC] += 10 + ivenarg[cdesc[WEAR]]; + break; + case OSSPLATE: + cdesc[AC] += 12 + ivenarg[cdesc[WEAR]]; + break; + } + + if (cdesc[SHIELD] >= 0) + if (iven[cdesc[SHIELD]] == OSHIELD) + cdesc[AC] += 2 + ivenarg[cdesc[SHIELD]]; + if (cdesc[WIELD] < 0) + cdesc[WCLASS] = 0; + else + { + i = ivenarg[cdesc[WIELD]]; + switch (iven[cdesc[WIELD]]) + { + case ODAGGER: + cdesc[WCLASS] = 3 + i; + break; + case OBELT: + cdesc[WCLASS] = 7 + i; + break; + case OSHIELD: + cdesc[WCLASS] = 8 + i; + break; + case OSPEAR: + cdesc[WCLASS] = 10 + i; + break; + case OBATTLEAXE: + cdesc[WCLASS] = 17 + i; + break; + case OGREATSWORD: + cdesc[WCLASS] = 19 + i; + break; + case OLONGSWORD: + cdesc[WCLASS] = 22 + i; + break; + case O2SWORD: + cdesc[WCLASS] = 26 + i; + break; + case OHSWORD: + cdesc[WCLASS] = 25 + i; + break; + case OSWORD: + cdesc[WCLASS] = 32 + i; + break; + case OSWORDofSLASHING: + cdesc[WCLASS] = 30 + i; + break; + case OHAMMER: + cdesc[WCLASS] = 35 + i; + break; + default: + cdesc[WCLASS] = 0; + } + } + cdesc[WCLASS] += cdesc[MOREDAM]; + + /* now for regeneration abilities based on rings */ + cdesc[REGEN] = 1; + cdesc[ENERGY] = 0; + j = 0; + for (k = 25; k > 0; k--) + if (iven[k]) + { + j = k; + k = 0; + } + for (i = 0; i <= j; i++) + { + switch (iven[i]) + { + case OPROTRING: + cdesc[AC] += ivenarg[i] + 1; + break; + case ODAMRING: + cdesc[WCLASS] += ivenarg[i] + 1; + break; + case OBELT: + cdesc[WCLASS] += ((ivenarg[i] << 1)) + 2; + break; + + case OREGENRING: + cdesc[REGEN] += ivenarg[i] + 1; + break; + case ORINGOFEXTRA: + cdesc[REGEN] += 5 * (ivenarg[i] + 1); + break; + case OENERGYRING: + cdesc[ENERGY] += ivenarg[i] + 1; + break; + } + } +} + + +/* +* quit() +* +* subroutine to ask if the player really wants to quit +*/ +void +quit (void) +{ + int i; + + cursors (); + strcpy (lastmonst, ""); + lprcat ("\nDo you really want to quit (all progress will be lost)?"); + for (;;) + { + i = ttgetch (); + if ((i == 'y') || (i == 'Y')) + { + died (300); + return; + } + if ((i == 'n') || (i == 'N') || (i == '\33')) + { + lprcat (" no"); + lflush (); + return; + } + } +} + + + +/* +* function to ask --more--. If the user enters a space, returns 0. If user +* enters Escape, returns 1. If user enters alphabetic, then returns that +* value. +*/ +int +more (char select_allowed) +{ + int i; + + lprcat ("\n --- press "); + lstandout ("space"); + lprcat (" to continue --- "); + if (select_allowed) + lprcat ("letter to select --- "); + + for (;;) + { + if ((i = ttgetch ()) == ' ' || i == '\n') + return 0; + if (i == '\x1B') + return 1; + if (select_allowed) + { + if (isupper (i)) + i = tolower (i); + if ((i >= 'a' && i <= 'z') || i == '.') + return i; + } + } +} + + + +/* +* function to enchant armor player is currently wearing +*/ +void +enchantarmor (void) +{ + int tmp; + + if (cdesc[WEAR] < 0) + { + if (cdesc[SHIELD] < 0) + { + cursors (); + lprcat ("\nYou feel a sense of loss"); + return; + } + else + { + tmp = iven[cdesc[SHIELD]]; + if (tmp != OSCROLL) + if (tmp != OPOTION) + { + ivenarg[cdesc[SHIELD]]++; + bottomline (); + } + } + } + tmp = iven[cdesc[WEAR]]; + if (tmp != OSCROLL) + if (tmp != OPOTION) + { + ivenarg[cdesc[WEAR]]++; + bottomline (); + } +} + + + +/* +* function to enchant a weapon presently being wielded +*/ +void +enchweapon (void) +{ + int tmp; + + if (cdesc[WIELD] < 0) + { + cursors (); + lprcat ("\nYou feel a sense of loss"); + return; + } + + tmp = iven[cdesc[WIELD]]; + + if (tmp != OSCROLL) + if (tmp != OPOTION) + { + ivenarg[cdesc[WIELD]]++; + if (tmp == OCLEVERRING) + cdesc[INTELLIGENCE]++; + else if (tmp == OSTRRING) + cdesc[STREXTRA]++; + else if (tmp == ODEXRING) + cdesc[DEXTERITY]++; + bottomline (); + } +} + + + +/* +* function to return 1 if a monster is next to the player else returns 0 +*/ +int +nearbymonst (void) +{ + int tmp, tmp2; + + for (tmp = playerx - 1; tmp < playerx + 2; tmp++) + for (tmp2 = playery - 1; tmp2 < playery + 2; tmp2++) + if (mitem[tmp][tmp2]) + return (1); /* if monster nearby */ + + return 0; +} + + + +/* +* function to steal an item from the players pockets +* returns 1 if steals something else returns 0 +*/ +int +stealsomething (void) +{ + int i, j; + + j = 100; + + for (;;) + { + + i = rund (26); + + if (iven[i] && cdesc[WEAR] != i && + cdesc[WIELD] != i && cdesc[SHIELD] != i) + { + + show3 (i); + adjustcvalues (iven[i], ivenarg[i]); + iven[i] = 0; + + return 1; + } + + if (--j <= 0) + { + + return 0; + } + } +} + + + +/* +* function to return 1 is player carrys nothing else return 0 +*/ +int +emptyhanded (void) +{ + int i; + + for (i = 0; i < 26; i++) + { + + if (iven[i] && i != cdesc[WIELD] && + i != cdesc[WEAR] && i != cdesc[SHIELD]) + { + + return 0; + } + } + + return 1; +} + + + +/* +* function to create a gem on a square near the player +*/ +void +creategem (void) +{ + int i, j; + + switch (rnd (4)) + { + case 1: + i = ODIAMOND; + j = 50; + break; + case 2: + i = ORUBY; + j = 40; + break; + case 3: + i = OEMERALD; + j = 30; + break; + default: + i = OSAPPHIRE; + j = 20; + break; + }; + + createitem (i, rnd (j) + j / 10); +} + + + +/* +* function to change character levels as needed when dropping an object +* that affects these characteristics +*/ +void +adjustcvalues (int itm, int arg) +{ + int flag; + + flag = 0; + + switch (itm) + { + case ODEXRING: + cdesc[DEXTERITY] -= arg + 1; + flag = 1; + break; + case OSTRRING: + cdesc[STREXTRA] -= arg + 1; + flag = 1; + break; + case OCLEVERRING: + cdesc[INTELLIGENCE] -= arg + 1; + flag = 1; + break; + case OHAMMER: + cdesc[DEXTERITY] -= 10; + cdesc[STREXTRA] -= 10; + cdesc[INTELLIGENCE] += 10; + flag = 1; + break; + case OSWORDofSLASHING: + cdesc[DEXTERITY] -= 5; + flag = 1; + break; + case OORBOFDRAGON: + --cdesc[SLAYING]; + return; + case OSPIRITSCARAB: + --cdesc[NEGATESPIRIT]; + return; + case OCUBEofUNDEAD: + --cdesc[CUBEofUNDEAD]; + return; + case ONOTHEFT: + --cdesc[NOTHEFT]; + return; + case OGREATSWORD: + cdesc[GREATSWORDDEATH] = 0; + return; + case OPOTION: + case OSCROLL: + return; + + default: + flag = 1; + }; + + if (flag) + { + + bottomline (); + } +} + +/* +* subroutine to get a yes or no response from the user +* returns y or n +*/ +char +getyn (void) +{ + int i = 0; + + while (i != 'y' && i != 'n' && i != '\33') + i = ttgetch (); + + return (char) i; +} + + + +/* +* function to calculate the pack weight of the player +* returns the number of pounds the player is carrying +*/ +int +packweight (void) +{ + int i, j = 25, k; + + k = cdesc[GOLD] / 1000; + while ((iven[j] == 0) && (j > 0)) + --j; + for (i = 0; i <= j; i++) + switch (iven[i]) + { + case 0: + break; + case OSSPLATE: + case OPLATEARMOR: + k += 40; + break; + case OPLATE: + k += 35; + break; + case OHAMMER: + k += 30; + break; + case OSPLINT: + k += 26; + break; + case OSWORDofSLASHING: + case OCHAIN: + case OBATTLEAXE: + case O2SWORD: + case OHSWORD: + k += 23; + break; + case OLONGSWORD: + case OSWORD: + case ORING: + k += 20; + break; + case OGREATSWORD: + case OSTUDLEATHER: + k += 15; + break; + case OLEATHER: + case OSPEAR: + k += 8; + break; + case OORBOFDRAGON: + case OBELT: + k += 4; + break; + case OSHIELD: + k += 7; + break; + case OCHEST: + k += 30 + ivenarg[i]; + break; + default: + k++; + break; + }; + return (k); +} diff --git a/src/help.c b/src/help.c new file mode 100644 index 0000000..bb290c7 --- /dev/null +++ b/src/help.c @@ -0,0 +1,137 @@ +/* help.c */ +#include +#include +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/ansiterm.h" +#include "includes/display.h" +#include "includes/help.h" +#include "includes/io.h" +#include "includes/sysdep.h" + +/* I coded a nice line-reader for this. Unfortunately it's very hard to get a good + * cursor and text position when reading a file from inside a curses program. + * so I did this instead, I actually prefer it. -Gibbon + + */ + +int +display_help_text(void) +{ + + screen_clear(); + lstandout("Help File for Larn\n"); + lprcat("b move southwest B run southwest A desecrate an altar\n"); + lprcat("c cast a spell C close a door Z teleport yourself \n"); + lprcat("d drop an item D drink at a fountain < go up stairs or \n"); + lprcat("e eat something E enter a store, dungeon volcanic shaft \n"); + lprcat("g get present pack weight > go down stairs or \n"); + lprcat("h move left H run left volcanic shaft \n"); + lprcat("i inventory your pockets I list all known items \n"); + lprcat("j move down J run down ^ identify a trap \n"); + lprcat("k move up K run up , pick up item \n"); + lprcat("l move right L run right : look at object you\n"); + lprcat("n move southeast N run southeast are standing on \n"); + lprcat(" O open a door or chest . stay here \n"); + lprcat("p pray at an altar P give tax status m move without picking\n"); + lprcat("q quaff a potion Q quit the game (suicide) up an object \n"); + lprcat("r read a scroll R remove gems from throne / identify objects in\n"); + lprcat("s sit on a throne S save the game the game \n"); + lprcat("t tidy up at a fountain T take off armor \n"); + lprcat("u move northeast U run northeast \n"); + lprcat("v print program version \n"); + lprcat("w wield a weapon W wear armor \n"); + lprcat("y move northwest Y run northwest ^L redraw the screen \n"); + retcont(); + screen_clear(); + lstandout("Special Notes\n"); + lprcat("When dropping gold, if you type '*' as your amount, all your gold gets dropped.\n"); + lprcat("In general, typing in '*' means all of what your interested in. This is true\n"); + lprcat("when visiting the bank, or when contributing at altars.\n\n"); + lprcat("When in the store, trading post, school, or home, an will get you out.\n\n"); + lprcat("When casting a spell, if you need a list of spells you can cast, type 'I' as\n"); + lprcat("the first letter of your spell. The available list of spells will be shown,\n"); + lprcat("after which you may enter the spell code. This only works on the 1st letter\n"); + lprcat("of the spell you are casting.\n\n"); + lprcat("When an inventory list is on the screen from a drop, quaff, read, or similar\n"); + lprcat("command, you can type the letter of the object that you wish to act apon,\n"); + lprcat("without having to type a space to get back to the prompt.\n\n"); + lprcat("If NumLock is off, the Keypad functions in the obvious way for movement. Hold\n"); + lprcat("Shift when pressing any direction on the Keypad to run in that direction. The\n"); + lprcat("5 key on the Keypad is the same as \"stay here\", which really means to\n"); + lprcat("skip your turn."); + retcont(); + screen_clear(); + lstandout("Explanation of the Larn scoreboard facility\n"); + lprcat("Larn supports TWO scoreboards, one for winners, and one for deceased\n"); + lprcat("characters. Each player (by the name entered when you start the game)\n"); + lprcat("is allowed one slot on each scoreboard, if the score is in the top ten for\n"); + lprcat("that scoreboard. This design helps insure that frequent players of Larn\n"); + lprcat("do not hog the scoreboard, and gives more players a chance for glory.\n\n"); + lprcat("Level of difficulty is also noted on the scoreboards, and this takes precedence\n"); + lprcat("over score for determining what entry is on the scoreboard. For example:\n"); + lprcat("if \"Yar, the Bug Slayer\" has a score of 128003 on the scoreboard at diff 0,\n"); + lprcat("then a game at diff 1 and a score of 4112 would replace the previous\n"); + lprcat("entry on the scoreboard. Note that when a player dies, the inventory is\n"); + lprcat("stored in the scoreboard so that everyone can see what items the player had\n"); + lprcat("at the time of death."); + return_to_game(); + drawscreen(); + setscroll(); + return(0); +} + +/* + * function to display the welcome message and background + */ +void +welcome (void) +{ + lprcat("Welcome to the game of Larn.\n\n At this moment, you face a great problem."); + lprcat("\nYour daughter has contracted a strange disease, and none of your"); + lprcat("\nhome remedies seem to have any effect. You sense that she is in"); + lprcat("\nmortal danger, and you must try to save her."); + lprcat("\n\n Time ago you heard of a land of great danger and opportunity."); + lprcat("\nPerhaps here is the solution you need."); + lprcat("\n\n It has been said that there once was a great magician who called himself"); + lprcat("\nPolinneaus. Many years ago after having many miraculous successes,"); + lprcat("\nPolinneaus retired to the caverns of Larn, where he devoted most of his"); + lprcat("\ntime to the creation of magic. Rumors have it that one day Polinneaus set"); + lprcat("\nout to dispel an attacking army in a forest some distance to the north."); + lprcat("\nIt is believed that here he met his demise. The caverns of Larn it is"); + lprcat("\nthought, must be magnificent in design and contain much magic and treasure."); + lprcat("\nOne option you have is to undertake a journey into these caverns in hope"); + lprcat("\nof finding a cure."); + lprcat("\n"); + lprcat("\n\nGood Luck! You're going to need it!"); + + /* press return to continue */ + retcont(); +} + +/* + * function to say press return to continue and reset scroll when done + */ +void +retcont(void) +{ + cursor(1, 24); + lprcat("Press "); + lstandout("return"); + lprcat(" to continue: "); + while (ttgetch() != '\n'); + setscroll (); +} + +/* Added for places where it points you back to the game. -Gibbon */ +void +return_to_game(void) +{ + cursor(1, 24); + lprcat("Press "); + lstandout("return"); + lprcat(" to go back to the game: "); + while (ttgetch() != '\n'); + setscroll(); +} diff --git a/src/includes/action.h b/src/includes/action.h new file mode 100644 index 0000000..3fd29a4 --- /dev/null +++ b/src/includes/action.h @@ -0,0 +1,27 @@ +void act_remove_gems (int); + +void act_sit_throne (int); + +void act_up_stairs (void); + +void act_down_stairs (void); + +void act_drink_fountain (void); + +void act_wash_fountain (void); + +void act_down_shaft (void); + +void act_up_shaft (void); + +void act_desecrate_altar (void); + +void act_donation_pray (void); + +void act_just_pray (void); + +void act_ignore_altar (void); + +void act_open_chest (int, int); + +int act_open_door (int, int); diff --git a/src/includes/ansiterm.h b/src/includes/ansiterm.h new file mode 100644 index 0000000..f84364c --- /dev/null +++ b/src/includes/ansiterm.h @@ -0,0 +1,8 @@ +void ansiterm_init (void); +void ansiterm_clean_up (void); + +void ansiterm_out (const char *, int); + +int ansiterm_getch (void); +int ansiterm_getche (void); +void ansiterm_delch (void); diff --git a/src/includes/bill.h b/src/includes/bill.h new file mode 100644 index 0000000..a6a8b8c --- /dev/null +++ b/src/includes/bill.h @@ -0,0 +1 @@ +void readmail (int); diff --git a/src/includes/create.h b/src/includes/create.h new file mode 100644 index 0000000..b48fa41 --- /dev/null +++ b/src/includes/create.h @@ -0,0 +1,4 @@ +void makeplayer (void); +void newcavelevel (int); +void eat (int, int); +int fillmonst (int); diff --git a/src/includes/diag.h b/src/includes/diag.h new file mode 100644 index 0000000..2a8198c --- /dev/null +++ b/src/includes/diag.h @@ -0,0 +1,7 @@ +#ifdef EXTRA +int diag (void); +void diagdrawscreen (void); +#endif + +int savegame (char *); +void restoregame (char *); diff --git a/src/includes/display.h b/src/includes/display.h new file mode 100644 index 0000000..3e555ea --- /dev/null +++ b/src/includes/display.h @@ -0,0 +1,13 @@ +void bottomline (void); +void bottomhp (void); +void bottomspell (void); +void bottomdo (void); +void bot_linex (void); +void bottomgold (void); +void draws (int, int, int, int); +void drawscreen (void); +void showcell (int, int); +void show1cell (int, int); +void showplayer (void); +int moveplayer (int); +void seemagic (int); diff --git a/src/includes/fortune.h b/src/includes/fortune.h new file mode 100644 index 0000000..acc27ee --- /dev/null +++ b/src/includes/fortune.h @@ -0,0 +1 @@ +void outfortune (void); diff --git a/src/includes/global.h b/src/includes/global.h new file mode 100644 index 0000000..d5192da --- /dev/null +++ b/src/includes/global.h @@ -0,0 +1,49 @@ +void raiselevel (void); + +void loselevel (void); + +void raiseexperience (long); + +void loseexperience (long); + +void losehp (int); + +void losemhp (int); + +void raisehp (int); + +void raisemhp (int); + +void raisemspells (int); + +void losemspells (int); + +int makemonst (int); + +void positionplayer (void); + +void recalc (void); + +void quit (void); + +int more (char); + +void enchantarmor (void); + +void enchweapon (void); + +int nearbymonst (void); + +int stealsomething (void); + +int emptyhanded (void); + +void creategem (void); + +void adjustcvalues (int, int); + +int getpassword (void); + +char getyn (void); + +int packweight (void); diff --git a/src/includes/help.h b/src/includes/help.h new file mode 100644 index 0000000..4d31ba6 --- /dev/null +++ b/src/includes/help.h @@ -0,0 +1,4 @@ +int display_help_text(void); +void welcome(void); +void retcont(void); +void return_to_game(void); diff --git a/src/includes/inventory.h b/src/includes/inventory.h new file mode 100644 index 0000000..991dc2e --- /dev/null +++ b/src/includes/inventory.h @@ -0,0 +1,23 @@ +void init_inventory (void); + +int showstr (char); + +int showwear (void); + +int showwield (void); + +int showread (void); + +int showeat (void); + +int showquaff (void); + +void show1 (int); + +int show3 (int); + +int take (int, int); + +int drop_object (int); + +int pocketfull (void); diff --git a/src/includes/io.h b/src/includes/io.h new file mode 100644 index 0000000..b9aa792 --- /dev/null +++ b/src/includes/io.h @@ -0,0 +1,63 @@ +void setupvt100 (void); +void clearvt100 (void); + +char ttgetch (void); + +void scbr (void); +void sncbr (void); + +void newgame (void); + +void lprintf (const char *, ...); + +void lprint (int); + +void lprc (char); + +void lwrite (char *, int); + +char lgetc (void); + +int larint (void); + +void lrfill (char *, int); + +char *lgetw (void); + +char *lgetl (void); + +int lcreat (char *); + +int lopen (char *); + +int lappend (char *); + +void lrclose (void); + +void lwclose (void); + +void lprcat (char *); + +void cursor (int, int); + +void cursors (void); + +void init_term (void); + +void cl_line (int, int); + +void cl_up (int, int); + +void cl_dn (int, int); + +void lstandout (char *); + +void set_score_output (void); + +void lflush (void); + +void lflushall (void); + +char *tmcapcnv (char *, char *); + +void enter_name (void); diff --git a/src/includes/larn.h b/src/includes/larn.h new file mode 100644 index 0000000..0b8a23d --- /dev/null +++ b/src/includes/larn.h @@ -0,0 +1,15 @@ +/* File created by Gibbon. +*/ + +/* global includes by all systems */ +#include +#include +#include +#include +#include + +/* includes of other header files */ +#include "larncons.h" +#include "larndata.h" +#include "larnfunc.h" +#include "ansiterm.h" diff --git a/src/includes/larncons.h b/src/includes/larncons.h new file mode 100644 index 0000000..cd26144 --- /dev/null +++ b/src/includes/larncons.h @@ -0,0 +1,336 @@ +/* +* +* constants +* +*/ + +#define VER 14 +#define SUBVER 1 +#define PATCHLEVEL 2 + +/* defines below are for use in the termcap mode only */ +#define ST_START 1 +#define ST_END 2 +#define BOLD 3 +#define END_BOLD 4 +#define CLEAR 5 +#define CL_LINE 6 +#define T_INIT 7 +#define T_END 8 +#define CL_DOWN 14 +#define CURSOR 15 + + + +#define KNOWNOT 0x00 +#define HAVESEEN 0x1 +#define KNOWHERE 0x2 +#define KNOWALL (HAVESEEN | KNOWHERE) + +#define PATHLEN 80 + +#define LARNHOME "" + +#ifndef WIZID +#define WIZID 1000 +#endif + +#define TRUE 1 +#define FALSE 0 + +#define MAXLEVEL 11 /* max # levels in the dungeon */ +#define MAXVLEVEL 3 /* max # of levels in the temple of the luran */ +#define MAXX 67 +#define MAXY 17 + +#define SCORESIZE 10 /* this is the number of people on a scoreboard max */ +#define MAXPLEVEL 100 /* maximum player level allowed */ +#define SPNUM 38 /* maximum number of spells in existance */ +#define TIMELIMIT 30000 /* maximum number of moves before the game is called */ +#define TAXRATE 1/20 /* tax rate for the LRS */ + +#define BUFBIG 4096 /* size of the output buffer */ +#define MAXIBUF 4096 /* size of the input buffer */ +#define LOGNAMESIZE 20 /* max size of the player's name */ + +#define SAVEFILENAMESIZE 128 /* max size of the savefile path */ + +#define STRING_BUFFER_SIZE 256 + + + +/* +* monster related constants +*/ +/* maximum # monsters in the dungeon */ +#define MAXMONST 56 + +/* defines for the monsters as objects */ +#define BAT 1 +#define GNOME 2 +#define HOBGOBLIN 3 +#define JACKAL 4 +#define KOBOLD 5 +#define ORC 6 +#define SNAKE 7 +#define CENTIPEDE 8 +#define JACULI 9 +#define TROGLODYTE 10 +#define ANT 11 +#define EYE 12 +#define LEPRECHAUN 13 +#define NYMPH 14 +#define QUASIT 15 +#define RUSTMONSTER 16 +#define ZOMBIE 17 +#define ASSASSINBUG 18 +#define BUGBEAR 19 +#define HELLHOUND 20 +#define ICELIZARD 21 +#define CENTAUR 22 +#define TROLL 23 +#define YETI 24 +#define WHITEDRAGON 25 +#define ELF 26 +#define CUBE 27 +#define METAMORPH 28 +#define VORTEX 29 +#define ZILLER 30 +#define VIOLETFUNGI 31 +#define WRAITH 32 +#define FORVALAKA 33 +#define LAMANOBE 34 +#define OSEQUIP 35 +#define ROTHE 36 +#define XORN 37 +#define VAMPIRE 38 +#define INVISIBLESTALKER 39 +#define POLTERGEIST 40 +#define DISENCHANTRESS 41 +#define SHAMBLINGMOUND 42 +#define YELLOWMOLD 43 +#define UMBERHULK 44 +#define GNOMEKING 45 +#define MIMIC 46 +#define WATERLORD 47 +#define BRONZEDRAGON 48 +#define GREENDRAGON 49 +#define PURPLEWORM 50 +#define XVART 51 +#define SPIRITNAGA 52 +#define SILVERDRAGON 53 +#define PLATINUMDRAGON 54 +#define GREENURCHIN 55 +#define REDDRAGON 56 +#define DEMONLORD 57 +#define DEMONPRINCE 64 + + + + +/* +* defines for the character attribute array cdesc[] +*/ +#define STRENGTH 0 /* characters physical strength not due to objects */ +#define INTELLIGENCE 1 +#define WISDOM 2 +#define CONSTITUTION 3 +#define DEXTERITY 4 +#define CHARISMA 5 +#define HPMAX 6 +#define HP 7 +#define GOLD 8 +#define EXPERIENCE 9 +#define LEVEL 10 +#define REGEN 11 +#define WCLASS 12 +#define AC 13 +#define BANKACCOUNT 14 +#define SPELLMAX 15 +#define SPELLS 16 +#define ENERGY 17 +#define ECOUNTER 18 +#define MOREDEFENSES 19 +#define WEAR 20 +#define PROTECTIONTIME 21 +#define WIELD 22 +#define AMULET 23 +#define REGENCOUNTER 24 +#define MOREDAM 25 +#define DEXCOUNT 26 +#define STRCOUNT 27 +#define BLINDCOUNT 28 +#define CAVELEVEL 29 +#define CONFUSE 30 +#define ALTPRO 31 +#define HERO 32 +#define CHARMCOUNT 33 +#define INVISIBILITY 34 +#define CANCELLATION 35 +#define HASTESELF 36 +#define EYEOFLARN 37 +#define AGGRAVATE 38 +#define GLOBE 39 +#define TELEFLAG 40 +#define SLAYING 41 +#define NEGATESPIRIT 42 +#define SCAREMONST 43 +#define AWARENESS 44 +#define HOLDMONST 45 +#define TIMESTOP 46 +#define HASTEMONST 47 +#define CUBEofUNDEAD 48 +#define GIANTSTR 49 +#define FIRERESISTANCE 50 +#define BESSMANN 51 +#define NOTHEFT 52 +#define HARDGAME 53 +#define CPUTIME 54 +#define BYTESIN 55 +#define BYTESOUT 56 +#define MOVESMADE 57 +#define MONSTKILLED 58 +#define SPELLSCAST 59 +#define GREATSWORDDEATH 60 +#define SPIRITPRO 61 +#define UNDEADPRO 62 +#define SHIELD 63 +#define STEALTH 64 +#define ITCHING 65 +#define LAUGHING 66 +#define DRAINSTRENGTH 67 +#define CLUMSINESS 68 +#define INFEEBLEMENT 69 +#define HALFDAM 70 +#define SEEINVISIBLE 71 +#define FILLROOM 72 +#define RANDOMWALK 73 +#define SPHCAST 74 /* nz if an active sphere of annihilation */ +#define WTW 75 /* walk through walls */ +#define STREXTRA 76 /* character strength due to objects or enchantments */ +#define TMP 77 /* misc scratch space */ +#define LIFEPROT 78 /* life protection counter */ + + +/* nap related */ +#define NAPTIME 1000 + + +/* +* object related constants +*/ +#define MAXSCROLL 28 /* maximum number of scrolls that are possible */ +#define MAXSCROLLNAME 32 + +#define MAXPOTION 35 /* maximum number of potions that are possible */ +#define MAXPOTIONNAME 32 + +#define MAXOBJ 93 /* the maximum number of objects n < MAXOBJ */ + +/* defines for the objects in the game */ +#define MAXOBJECT 92 + +#define OALTAR 1 +#define OTHRONE 2 +#define OORB 3 +#define OPIT 4 +#define OSTAIRSUP 5 +#define OELEVATORUP 6 +#define OFOUNTAIN 7 +#define OSTATUE 8 +#define OTELEPORTER 9 +#define OSCHOOL 10 +#define OMIRROR 11 +#define ODNDSTORE 12 +#define OSTAIRSDOWN 13 +#define OELEVATORDOWN 14 +#define OBANK2 15 +#define OBANK 16 +#define ODEADFOUNTAIN 17 +#define OMAXGOLD 70 +#define OGOLDPILE 18 +#define OOPENDOOR 19 +#define OCLOSEDDOOR 20 +#define OWALL 21 +#define OTRAPARROW 66 +#define OTRAPARROWIV 67 + +#define OLARNEYE 22 + +#define OPLATE 23 +#define OCHAIN 24 +#define OLEATHER 25 +#define ORING 60 +#define OSTUDLEATHER 61 +#define OSPLINT 62 +#define OPLATEARMOR 63 +#define OSSPLATE 64 +#define OSHIELD 68 +#define OELVENCHAIN 92 + + +#define OSWORDofSLASHING 26 +#define OHAMMER 27 +#define OSWORD 28 +#define O2SWORD 29 +#define OHSWORD 59 +#define OSPEAR 30 +#define ODAGGER 31 +#define OBATTLEAXE 57 +#define OLONGSWORD 58 +#define OGREATSWORD 65 +#define OVORPAL 90 +#define OSLAYER 91 + +#define ORINGOFEXTRA 32 +#define OREGENRING 33 +#define OPROTRING 34 +#define OENERGYRING 35 +#define ODEXRING 36 +#define OSTRRING 37 +#define OCLEVERRING 38 +#define ODAMRING 39 + +#define OBELT 40 + +#define OSCROLL 41 +#define OPOTION 42 +#define OBOOK 43 +#define OCHEST 44 +#define OAMULET 45 + +#define OORBOFDRAGON 46 +#define OSPIRITSCARAB 47 +#define OCUBEofUNDEAD 48 +#define ONOTHEFT 49 + +#define ODIAMOND 50 +#define ORUBY 51 +#define OEMERALD 52 +#define OSAPPHIRE 53 + +#define OENTRANCE 54 +#define OVOLDOWN 55 +#define OVOLUP 56 +#define OHOME 69 + +#define OKGOLD 71 +#define ODGOLD 72 +#define OIVDARTRAP 73 +#define ODARTRAP 74 +#define OTRAPDOOR 75 +#define OIVTRAPDOOR 76 +#define OTRADEPOST 77 +#define OIVTELETRAP 78 +#define ODEADTHRONE 79 +#define OANNIHILATION 80 /* sphere of annihilation */ +#define OTHRONE2 81 +#define OLRS 82 /* Larn Revenue Service */ +#define OCOOKIE 83 +#define OURN 84 +#define OBRASSLAMP 85 +#define OHANDofFEAR 86 /* hand of fear */ +#define OSPHTAILSMAN 87 /* tailsman of the sphere */ +#define OWWAND 88 /* wand of wonder */ +#define OPSTAFF 89 /* staff of power */ +/* used up to 93. ~Gibbon */ diff --git a/src/includes/larndata.h b/src/includes/larndata.h new file mode 100644 index 0000000..f73fe05 --- /dev/null +++ b/src/includes/larndata.h @@ -0,0 +1,175 @@ +/* Larn is copyrighted 1986 by Noah Morgan. */ + +#include + +/* +* +* types +* +*/ + +/* this is the structure that holds the entire dungeon specifications */ +struct cel +{ + int hitp; /* monster's hit points */ + int mitem; /* the monster ID */ + int item; /* the object's ID */ + int iarg; /* the object's argument */ + int know; /* have we been here before */ +}; + +/* this is the structure for maintaining & moving the spheres of annihilation */ +struct sphere +{ + struct sphere *p; /* pointer to next structure */ + int x, y, lev; /* location of the sphere */ + int dir; /* direction sphere is going in */ + int lifetime; /* duration of the sphere */ +}; + + +/* this is the structure definition of the monster data +*/ +struct monst +{ + char *name; + int level; + int armorclass; + int damage; + int attack; + int defense; + int genocided; + int intelligence; /* monsters intelligence -- used to choose movement */ + int gold; + int hitpoints; + unsigned long experience; +}; + +/* this is the structure definition for the items in the dnd store */ +struct _itm +{ + int price; + int obj; + int arg; + int qty; +}; + + +/* +* +* data declarations +* +*/ +extern int regen_bottom; +extern char floorc, wallc; + +/* extern char for the water. -Gibbon */ +extern char waterc; + +extern int VERSION, SUBVERSION; +extern int beenhere[], cheat; +extern int course[]; +extern int item[MAXX][MAXY], iven[], know[MAXX][MAXY]; + +extern char aborted[]; +extern char *classname[]; +extern char lastmonst[]; +extern char *lpnt, *lpbuf, *lpend, *inbuffer; + +extern int level; +extern int mitem[MAXX][MAXY], monstlevel[]; +extern int nch[], ndgg[], nlpts[], nomove; +extern int nplt[], nsw[]; +extern int potprob[]; + +extern char monstnamelist[]; +extern char *levelname[]; +extern char objnamelist[]; +extern char logname[]; + +extern char mazefile[]; +extern char diagfile[]; +extern char fortfile[]; +extern char helpfile[]; +extern char logfile[]; +extern char playerids[]; + +extern int predostuff, restorflag; +extern char savefilename[]; +extern char scorefile[]; +extern int scprob[]; +extern int screen[MAXX][MAXY], sex; +extern int spelknow[]; + +extern char *spelmes[]; +extern char *speldescript[]; +extern char *spelcode[]; + + + +extern char *spelname[]; + + +extern int splev[], stealth[MAXX][MAXY], wizard; +extern int diroffx[], diroffy[], hitflag, hit2flag, hit3flag, + hitp[MAXX][MAXY]; +extern int iarg[MAXX][MAXY], ivenarg[], lasthx, lasthy, lastnum, lastpx, + lastpy; +extern int oldx, oldy, playerx, playery; +extern int enable_scroll, yrepcount, wisid, lfd, fd; +extern long outstanding_taxes, skill[], gtime; +extern long cdesc[], cbak[]; +extern time_t initialtime; +extern unsigned long lrandx; +extern struct cel *cell; +extern struct sphere *spheres; + + +extern struct monst monster[]; + +extern struct _itm dnd_item[]; + + + +/* +* config.c +*/ +extern char *password; + + + +/* +* data.c +*/ +extern int prayed; + +extern char scrollname[MAXSCROLL + 1][MAXSCROLLNAME]; +extern char potionname[MAXPOTION + 1][MAXPOTIONNAME]; + +extern char *objectname[]; + + +extern int spelweird[MAXMONST + 8][SPNUM]; + + + +/* +* main.c +*/ +extern int rmst; +extern int dropflag; +extern int save_mode; + + +/* +* store.c +*/ +extern int lasttime; + + +/* +* tok.c +*/ +extern int move_no_pickup; + +//extern int larn_die(char, char *); diff --git a/src/includes/larnfunc.h b/src/includes/larnfunc.h new file mode 100644 index 0000000..b03a2f4 --- /dev/null +++ b/src/includes/larnfunc.h @@ -0,0 +1,84 @@ + + +/* +* +* macros +* +*/ + +/* macro to create scroll #'s with probability of occurrence */ +#define newscroll() (scprob[rund(81)]) + +/* macro to return a potion # created with probability of occurrence */ +#define newpotion() (potprob[rund(41)]) + +/* macro to return the + points on created leather armor */ +#define newleather() (nlpts[rund(cdesc[HARDGAME]?13:15)]) + +/* macro to return the + points on chain armor */ +#define newchain() (nch[rund(10)]) + +/* macro to return + points on plate armor */ +#define newplate() (nplt[rund(cdesc[HARDGAME]?4:12)]) + +/* macro to return + points on new daggers */ +#define newdagger() (ndgg[rund(13)]) + +/* macro to return + points on new swords */ +#define newsword() (nsw[rund(cdesc[HARDGAME]?6:13)]) + +/* macro to destroy object at present location */ +#define forget() (item[playerx][playery]=know[playerx][playery]=0) + +/* macro to wipe out a monster at a location */ +#define disappear(x,y) (mitem[x][y]=know[x][y]=0) + +/* macro to turn on bold display for the terminal */ +#define setbold() (*lpnt++ = ST_START) + +/* macro to turn off bold display for the terminal */ +#define resetbold() (*lpnt++ = ST_END) + +/* macro to setup the scrolling region for the terminal */ +#define setscroll() enable_scroll=1 + +/* macro to clear the scrolling region for the terminal */ +#define resetscroll() enable_scroll=0 + +/* macro to clear the screen and home the cursor */ +#define screen_clear() (*lpnt++ =CLEAR, regen_bottom=TRUE) + +/* macro to clear to end of line */ +#define cltoeoln() (*lpnt++ = CL_LINE) + +/* macros to seed the random number generator */ +/* This is needed on Windows which throws an error due to 'random' not being defined on MingW. I'll clean it up later. -Gibbon */ +#define srandom srand +#define random rand + +/* New random seed function. -Gibbon */ +#define srand srandom +#define rand random + +#define rnd(x) ((int)(rand() % (x)) + 1) +#define rund(x) ((int)(rand() % (x))) + +/* min/max */ +#ifndef min +#define min(x,y) (((x)>(y))?(y):(x)) +#endif +#ifndef max +#define max(x,y) (((x)>(y))?(x):(y)) +#endif + + + +/* +* +* function declarations +* +*/ + +#ifndef WINDOWS +#define _getch ansiterm_getch +#endif diff --git a/src/includes/main.h b/src/includes/main.h new file mode 100644 index 0000000..57d8923 --- /dev/null +++ b/src/includes/main.h @@ -0,0 +1,3 @@ +void parse2 (void); + +int readnum (int); diff --git a/src/includes/monster.h b/src/includes/monster.h new file mode 100644 index 0000000..17d4e5a --- /dev/null +++ b/src/includes/monster.h @@ -0,0 +1,19 @@ +void checkloss (int); + +void createitem (int, int); + +void createmonster (int); + +void dropgold (int); + +int hitm (int, int, int); + +void hitmonster (int, int); + +void hitplayer (int, int); + +int newobject (int, int *); + +void something (int); + +int vxy (int *, int *); diff --git a/src/includes/moreobj.h b/src/includes/moreobj.h new file mode 100644 index 0000000..7a753fe --- /dev/null +++ b/src/includes/moreobj.h @@ -0,0 +1,35 @@ +void oaltar (void); + +void othrone (int); + +void odeadthrone (void); + +void ochest (void); + +void ofountain (void); + +void fntchange (int); + +void drink_fountain (void); + +void wash_fountain (void); + +void enter (void); + +void remove_gems (void); + +void sit_on_throne (void); + +void up_stairs (void); + +void down_stairs (void); + +void open_something (void); + +void close_something (void); + +void desecrate_altar (void); + +void pray_at_altar (void); + +void specify_object (void); diff --git a/src/includes/movem.h b/src/includes/movem.h new file mode 100644 index 0000000..6d0b337 --- /dev/null +++ b/src/includes/movem.h @@ -0,0 +1 @@ +void movemonst (void); diff --git a/src/includes/object.h b/src/includes/object.h new file mode 100644 index 0000000..7e48c3e --- /dev/null +++ b/src/includes/object.h @@ -0,0 +1,17 @@ +void lookforobject (char, char, char); + +void oteleport (int); + +void quaffpotion (int, int); + +void adjtimel (int); + +void read_scroll (int); + +void readbook (int); + +void ohome (void); + +void iopts (void); + +void ignore (void); diff --git a/src/includes/regen.h b/src/includes/regen.h new file mode 100644 index 0000000..2c37294 --- /dev/null +++ b/src/includes/regen.h @@ -0,0 +1 @@ +void regen (void); diff --git a/src/includes/savelev.h b/src/includes/savelev.h new file mode 100644 index 0000000..44b5279 --- /dev/null +++ b/src/includes/savelev.h @@ -0,0 +1,3 @@ +void savelevel (void); + +void getlevel (void); diff --git a/src/includes/scores.h b/src/includes/scores.h new file mode 100644 index 0000000..317733e --- /dev/null +++ b/src/includes/scores.h @@ -0,0 +1,17 @@ +int makeboard (void); + +int hashewon (void); + +void checkmail (void); + +int paytaxes (int); + +void showscores (void); + +void showallscores (void); + +void died (int); + +void diedlog (void); + +int getplid (char *); diff --git a/src/includes/spells.h b/src/includes/spells.h new file mode 100644 index 0000000..6b392fd --- /dev/null +++ b/src/includes/spells.h @@ -0,0 +1,11 @@ +void cast (void); + +int fullhit (int); + +void godirect (int, int, char *, int, char); + +void ifblind (int, int); + +int dirsub (int *, int *); + +void annihilate (void); diff --git a/src/includes/spheres.h b/src/includes/spheres.h new file mode 100644 index 0000000..98b58ce --- /dev/null +++ b/src/includes/spheres.h @@ -0,0 +1,5 @@ +int newsphere (int, int, int, int); + +int rmsphere (int, int); + +void movsphere (void); diff --git a/src/includes/store.h b/src/includes/store.h new file mode 100644 index 0000000..190c015 --- /dev/null +++ b/src/includes/store.h @@ -0,0 +1,12 @@ +void dndstore (void); + +void oschool (void); + +void obank (void); +void obank2 (void); + +void ointerest (void); + +void otradepost (void); + +void olrs (void); diff --git a/src/includes/sysdep.h b/src/includes/sysdep.h new file mode 100644 index 0000000..9e1b119 --- /dev/null +++ b/src/includes/sysdep.h @@ -0,0 +1 @@ +void nap(int milliseconds); diff --git a/src/includes/tgoto.h b/src/includes/tgoto.h new file mode 100644 index 0000000..ab7f91d --- /dev/null +++ b/src/includes/tgoto.h @@ -0,0 +1 @@ +const char *atgoto (const char *, int, int); diff --git a/src/includes/tok.h b/src/includes/tok.h new file mode 100644 index 0000000..fa32de7 --- /dev/null +++ b/src/includes/tok.h @@ -0,0 +1,3 @@ +int yylex (void); +void sethard (int); + diff --git a/src/inventory.c b/src/inventory.c new file mode 100644 index 0000000..b005985 --- /dev/null +++ b/src/inventory.c @@ -0,0 +1,772 @@ +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/inventory.h" +#include "includes/io.h" +#include + +static int qshowstr (char); + +static void t_setup (int); +static void t_endup (int); + +static int show2 (int); + + + +/* Allow only 26 items (a to z) in the player's inventory */ +#define MAXINVEN 26 + +/* The starting limit to the number of items the player can carry. +The limit should probably be based on player strength and the +weight of the items. +*/ +#define MIN_LIMIT 15 + +/* define a sentinel to place at the end of the sorted inventory. +(speeds up display reads ) +*/ +#define END_SENTINEL 255 + +/* declare the player's inventory. These should only be referenced +in this module. +iven - objects in the player's inventory +ivenarg - attribute of each item ( + values, etc ) +ivensort - sorted inventory (so we don't sort each time) +*/ +int iven[MAXINVEN]; + +int ivenarg[MAXINVEN]; + +int ivensort[MAXINVEN + 1]; /* extra is for sentinel */ + + +static char srcount = 0; /* line counter for showstr() */ + + + +/* +* Initialize the player's inventory +*/ +void +init_inventory (void) +{ + int i; + + for (i = 0; i < MAXINVEN; i++) + { + iven[i] = ivenarg[i] = 0; + ivensort[i] = END_SENTINEL; + } + ivensort[MAXINVEN] = END_SENTINEL; + + /* For zero difficulty games, start the player out with armor and weapon. + We can sort the inventory right away because a dagger is 'later' than + leather armor. + */ + if (cdesc[HARDGAME] <= 0) + { + iven[0] = OLEATHER; + iven[1] = ODAGGER; + ivenarg[0] = ivenarg[1] = cdesc[WEAR] = ivensort[0] = 0; + ivensort[1] = cdesc[WIELD] = 1; + } +} + + + +/* +* show character's inventory +*/ +int +showstr (char select_allowed) +{ + int i, number, item_select; + + for (number = 3, i = 0; i < MAXINVEN; i++) + if (iven[i]) + number++; /* count items in inventory */ + t_setup (number); + item_select = qshowstr (select_allowed); + t_endup (number); + + return item_select; +} + + + +static int +qshowstr (char select_allowed) +{ + int i, j, k, itemselect = 0; + + srcount = 0; + + if (cdesc[GOLD]) + { + lprintf (".) %d gold pieces", (int) cdesc[GOLD]); + srcount++; + } + for (k = (MAXINVEN - 1); k >= 0; k--) + if (iven[k]) + { + for (i = 22; i < 84; i++) + for (j = 0; j <= k; j++) + if (i == iven[j]) + { + itemselect = show2 (j); + if (itemselect && select_allowed) + goto quitit; + } + k = 0; + } + + /*lprintf("\nElapsed time is %d. You have %d mobuls left",(int)((gtime+99)/100+1),(int)((TIMELIMIT-gtime)/100)); */ + lprintf ("\nElapsed time is %d. You have %d mobuls left", gtime / 100, + (TIMELIMIT - gtime) / 100); + itemselect = more (select_allowed); +quitit: + if (select_allowed) + return ((itemselect > 0) ? itemselect : 0); + else + return (0); +} + + + +/* +* subroutine to clear screen depending on # lines to display +*/ +static void +t_setup (int count) +{ + + /* how do we clear the screen? */ + if (count < 20) + { + + cl_up (79, count); + cursor (1, 1); + + } + else + { + + resetscroll (); + screen_clear(); + } +} + + + +/* +* subroutine to restore normal display screen depending on t_setup() +*/ +static void +t_endup (int count) +{ + + /* how did we clear the screen? */ + if (count < 18) + { + + draws (0, MAXX, 0, (count > MAXY) ? MAXY : count); + + } + else + { + + drawscreen (); + setscroll (); + } +} + + + +/* +* function to show the things player is wearing only +*/ +int +showwear (void) +{ + int i, count, itemselect; + + itemselect = 0; + + srcount = 0; + + for (count = 2, i = 0; i < MAXINVEN; i++) + { + + switch (iven[i]) + { + + case OLEATHER: + case OPLATE: + case OCHAIN: + case ORING: + case OSTUDLEATHER: + case OSPLINT: + case OPLATEARMOR: + case OSSPLATE: + case OSHIELD: + ++count; + break; + } + } + + t_setup (count); + + for (i = 0; i < MAXINVEN; i++) + { + + switch (iven[i]) + { + + case OLEATHER: + case OPLATE: + case OCHAIN: + case ORING: + case OSTUDLEATHER: + case OSPLINT: + case OPLATEARMOR: + case OSSPLATE: + case OSHIELD: + itemselect = show2 (i); + break; + } + + if (itemselect) + break; + } + + if (!itemselect) + { + + itemselect = more (TRUE); + } + + t_endup (count); + + if (itemselect > 1) + { + + return itemselect; + + } + + return 0; +} + + + +/* +* function to show the things player can wield only +*/ +int +showwield (void) +{ + int i, count, itemselect; + + itemselect = 0; + + srcount = 0; + + for (count = 2, i = 0; i < MAXINVEN; i++) + { + if (iven[i] != 0) + { + switch (iven[i]) + { + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + case OBOOK: + case OCHEST: + case OLARNEYE: + case ONOTHEFT: + case OSPIRITSCARAB: + case OCUBEofUNDEAD: + case OPOTION: + case OSCROLL: + break; + + default: + ++count; + } + } + } + + t_setup (count); + + for (i = 0; i < MAXINVEN; i++) + { + if (iven[i] != 0) + { + switch (iven[i]) + { + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + case OBOOK: + case OCHEST: + case OLARNEYE: + case ONOTHEFT: + case OSPIRITSCARAB: + case OCUBEofUNDEAD: + case OPOTION: + case OSCROLL: + break; + + default: + itemselect = show2 (i); + } + } + + if (itemselect) + break; + } + + if (!itemselect) + { + + itemselect = more (TRUE); + } + + t_endup (count); + + if (itemselect > 1) + { + + return itemselect; + + } + + return 0; +} + + + +/* +* function to show the things player can read only +*/ +int +showread (void) +{ + int i, count, itemselect; + + itemselect = 0; + + srcount = 0; + + for (count = 2, i = 0; i < MAXINVEN; i++) + { + + switch (iven[i]) + { + + case OBOOK: + case OSCROLL: + ++count; + break; + } + } + + t_setup (count); + + for (i = 0; i < MAXINVEN; i++) + { + + switch (iven[i]) + { + + case OBOOK: + case OSCROLL: + itemselect = show2 (i); + break; + } + + if (itemselect) + break; + } + + if (!itemselect) + { + + itemselect = more (TRUE); + } + + t_endup (count); + + if (itemselect > 1) + { + + return itemselect; + + } + + return 0; +} + + + +/* +* function to show the things player can eat only +*/ +int +showeat (void) +{ + int i, count, itemselect; + + itemselect = 0; + + srcount = 0; + + for (count = 2, i = 0; i < MAXINVEN; i++) + { + + switch (iven[i]) + { + + case OCOOKIE: + ++count; + break; + } + } + + t_setup (count); + + for (i = 0; i < MAXINVEN; i++) + { + + switch (iven[i]) + { + + case OCOOKIE: + itemselect = show2 (i); + break; + } + + if (itemselect) + break; + } + + if (!itemselect) + { + + itemselect = more (TRUE); + } + + t_endup (count); + + if (itemselect > 1) + { + + return itemselect; + + } + + return 0; +} + + + +/* +* function to show the things player can quaff only +*/ +int +showquaff (void) +{ + int i, count, itemselect; + + itemselect = 0; + + srcount = 0; + + for (count = 2, i = 0; i < MAXINVEN; i++) + { + + switch (iven[i]) + { + + case OPOTION: + ++count; + break; + } + } + + t_setup (count); + + for (i = 0; i < MAXINVEN; i++) + { + + switch (iven[i]) + { + + case OPOTION: + itemselect = show2 (i); + break; + } + + if (itemselect) + break; + } + + if (!itemselect) + { + + itemselect = more (TRUE); + } + + t_endup (count); + + if (itemselect > 1) + { + + return itemselect; + + } + + return 0; +} + +/*some backporting from 12.3 with my own changes and fixes. + *Cleaned up and squashed a bug with duplication of text shown. ~Gibbon + */ +void +show1 (int idx) +{ + lprc ('\n'); + cltoeoln (); + + /*not totally needed but it is cleaner. Inventory will not be == 0 due to starting items. ~Gibbon + */ + if (iven[idx] != 0) + { + attron(COLOR_PAIR(4)); + lprintf ("%c) ",idx + 'a'); + attroff(COLOR_PAIR(4)); + lprintf("%s", objectname[iven[idx]]); + } + + /*we can remove the index to object name and concatenate the above with the below for scrolls and potions. + *since OPOTION and OSCROLL (object names) will be stuffed into the above, we can focus on identified names. ~Gibbon + */ + if (iven[idx] == OPOTION && potionname[ivenarg[idx]][0] != '\0') + { + lprintf (" of%s", potionname[ivenarg[idx]]); + } + else if (iven[idx] == OSCROLL && scrollname[ivenarg[idx]][0] != '\0') + { + lprintf (" of%s", scrollname[ivenarg[idx]]); + } +} + +int +show3 (int index) +{ + + srcount = 0; + + return show2 (index); +} + +static int +show2 (int index) +{ + int itemselect = 0; + + switch (iven[index]) + { + case OPOTION: + case OSCROLL: + case OLARNEYE: + case OBOOK: + case OSPIRITSCARAB: + case ODIAMOND: + case ORUBY: + case OCUBEofUNDEAD: + case OEMERALD: + case OCHEST: + case OCOOKIE: + case OSAPPHIRE: + case ONOTHEFT: + show1 (index); + break; + + default: + lprc ('\n'); + cltoeoln (); + attron(COLOR_PAIR(4)); + lprintf ("%c) ",index + 'a'); + attroff(COLOR_PAIR(4)); + lprintf("%s",objectname[iven[index]]); + if (ivenarg[index] > 0) + lprintf (" + %d", (long) ivenarg[index]); + else if (ivenarg[index] < 0) + lprintf (" %d", (long) ivenarg[index]); + break; + } + if (cdesc[WIELD] == index) + lprcat (" (weapon in hand)"); + if ((cdesc[WEAR] == index) || (cdesc[SHIELD] == index)) + lprcat (" (being worn)"); + if (++srcount >= 22) + { + srcount = 0; + itemselect = more (TRUE); + screen_clear(); + } + return (itemselect); +} + + +/* +* function to put something in the players inventory +* returns 0 if success, 1 if a failure +*/ +int +take (int itm, int arg) +{ + int i, limit; + + /* cursors(); */ + if ((limit = 15 + (cdesc[LEVEL] >> 1)) > MAXINVEN) + limit = MAXINVEN; + for (i = 0; i < limit; i++) + if (iven[i] == 0) + { + iven[i] = itm; + ivenarg[i] = arg; + limit = 0; + switch (itm) + { + case OPROTRING: + case ODAMRING: + case OBELT: + limit = 1; + break; + case ODEXRING: + cdesc[DEXTERITY] += ivenarg[i] + 1; + limit = 1; + break; + case OSTRRING: + cdesc[STREXTRA] += ivenarg[i] + 1; + limit = 1; + break; + case OCLEVERRING: + cdesc[INTELLIGENCE] += ivenarg[i] + 1; + limit = 1; + break; + case OHAMMER: + cdesc[DEXTERITY] += 10; + cdesc[STREXTRA] += 10; + cdesc[INTELLIGENCE] -= 10; + limit = 1; + break; + + case OORBOFDRAGON: + cdesc[SLAYING]++; + break; + case OSPIRITSCARAB: + cdesc[NEGATESPIRIT]++; + break; + case OCUBEofUNDEAD: + cdesc[CUBEofUNDEAD]++; + break; + case ONOTHEFT: + cdesc[NOTHEFT]++; + break; + case OSWORDofSLASHING: + cdesc[DEXTERITY] += 5; + limit = 1; + break; + }; + lprcat ("\nYou pick up:"); + show3 (i); + if (limit) + bottomline (); + return (0); + } + lprcat ("\nYou can't carry anything else"); + return (1); +} + + + +/* +* subroutine to drop an object returns 1 if something there already else 0 +*/ +int +drop_object (int k) +{ + int itm; + + if ((k < 0) || (k >= MAXINVEN)) + return (0); + itm = iven[k]; + cursors (); + if (itm == 0) + { + lprintf ("\nYou don't have item %c! ", k + 'a'); + return (1); + } + if (item[playerx][playery]) + { + lprintf ("\nThere's something here already: %s", + objectname[item[playerx][playery]]); + dropflag = 1; + return (1); + } + if (playery == MAXY - 1 && playerx == 33) + return (1); /* not in entrance */ + item[playerx][playery] = itm; + iarg[playerx][playery] = ivenarg[k]; + lprcat ("\n You drop:"); + show3 (k); /* show what item you dropped */ + know[playerx][playery] = 0; + iven[k] = 0; + if (cdesc[WIELD] == k) + cdesc[WIELD] = -1; + if (cdesc[WEAR] == k) + cdesc[WEAR] = -1; + if (cdesc[SHIELD] == k) + cdesc[SHIELD] = -1; + adjustcvalues (itm, ivenarg[k]); + dropflag = 1; /* say dropped an item so wont ask to pick it up right away */ + return (0); +} + +/* +* routine to tell if player can carry one more thing +* returns 1 if pockets are full, else 0 +*/ +int +pocketfull (void) +{ + int i, limit; + + limit = MIN_LIMIT + (cdesc[LEVEL] >> 1); + + if (limit > MAXINVEN) + { + + limit = MAXINVEN; + } + + for (i = 0; i < limit; i++) + { + + if (iven[i] == 0) + { + + return 0; + } + } + + return 1; +} diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..e280a02 --- /dev/null +++ b/src/io.c @@ -0,0 +1,1174 @@ +/* io.c +* +* setupvt100() Subroutine to set up terminal in correct mode for game +* clearvt100() Subroutine to clean up terminal when the game is over +* ttgetch() Routine to read in one character from the terminal +* scbr() Function to set cbreak -echo for the terminal +* sncbr() Function to set -cbreak echo for the terminal +* newgame() Subroutine to save the initial time and seed rnd() +* +* FILE OUTPUT ROUTINES +* +* lprintf(format,args . . .) printf to the output buffer +* lprint(integer) send binary integer to output buffer +* lwrite(buf,len) write a buffer to the output buffer +* lprcat(str) sent string to output buffer +* +* FILE OUTPUT MACROS +* +* lprc(character) put the character into the output buffer +* +* FILE INPUT ROUTINES +* +* int lgetc() read one character from input buffer +* int larint() read one integer from input buffer +* lrfill(address,number) put input bytes into a buffer +* char *lgetw() get a whitespace ended word from input +* char *lgetl() get a \n or EOF ended line from input +* +* FILE OPEN / CLOSE ROUTINES +* +* lcreat(filename) create a new file for write +* lopen(filename) open a file for read +* lappend(filename) open for append to an existing file +* lrclose() close the input file +* lwclose() close output file +* lflush() flush the output buffer +* +* Other Routines +* +* cursor(x,y) position cursor at [x,y] +* cursors() position cursor at [1,24] (saves memory) +* cl_line(x,y) Clear line at [1,y] and leave cursor at [x,y] +* cl_up(x,y) Clear screen from [x,1] to current line. +* cl_dn(x,y) Clear screen from [1,y] to end of display. +* lstandout(str) Print the string in standout mode. +* set_score_output() Called when output should be literally printed. +** ttputch(ch) Print one character in decoded output buffer. +** flush_buf() Flush buffer with decoded output. +** init_term() Terminal initialization +** char *tmcapcnv(sd,ss) Routine to convert VT100 \33's to termcap format +* +* Note: ** entries are available only in termcap mode. +*/ +#include +#include +#include +#include +#include + +#if defined WINDOWS || WINDOWS_VS +#include +#include +#endif + +#include +#include +#include /* For O_BINARY */ + +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/ansiterm.h" + +#if defined NIX +#include +#include +#ifndef FIONREAD +#include +#endif +#endif +#include "includes/display.h" +#include "includes/global.h" +#include "includes/io.h" +#include "includes/monster.h" +#include "includes/scores.h" +#include "includes/tgoto.h" + +#define LINBUFSIZE 128 /* size of the lgetw() and lgetl() buffer */ +int lfd; /* output file numbers */ +int fd; /* input file numbers */ + + +static int ipoint = MAXIBUF, iepoint = MAXIBUF; /* input buffering pointers */ +static char lgetwbuf[LINBUFSIZE]; /* get line (word) buffer */ + + +static int (*getchfn) (void); + +static void ttputch (int); +static void tputs (const char *); + +static void flush_buf (void); + +/* +* Subroutine to set up terminal in correct mode for game +* +* Attributes off, clear screen, set scrolling region, set tty mode +*/ +void +setupvt100 (void) +{ + + screen_clear(); + + setscroll (); + + scbr (); /* system("stty cbreak -echo"); */ +} + + + +/* +* Subroutine to clean up terminal when the game is over +* +* Attributes off, clear screen, unset scrolling region, restore tty mode +*/ +void +clearvt100 (void) +{ + + ansiterm_clean_up (); + + resetscroll (); + + /* clear(); *//* why does this routine need to clear() ? */ + + sncbr (); /* system("stty -cbreak echo"); */ +} + + + +/* +* ttgetch() Routine to read in one character from the terminal +*/ +char +ttgetch (void) +{ + char byt; + +#ifdef EXTRA + cdesc[BYTESIN]++; +#endif + + lflush (); /* be sure output buffer is flushed */ + + byt = (char) (*getchfn) (); + + if (byt == '\r') + { + + byt = '\n'; + } + + return byt; +} + + +/* +* scbr() Function to set cbreak -echo for the terminal +* +* like: system("stty cbreak -echo") +*/ +void +scbr (void) +{ + + /* + * Set up to use the direct console input call which may + * read from the keypad; + */ + getchfn = ansiterm_getch; +} + + + +/* +* sncbr() Function to set -cbreak echo for the terminal +* +* like: system("stty -cbreak echo") +*/ +void +sncbr (void) +{ + + /* + * Set up to use the direct console input call with echo, getche() + */ + getchfn = ansiterm_getche; +} + + + +/* +* newgame() Subroutine to save the initial time and seed rnd() +*/ +void +newgame (void) +{ + long *p, *pe; + + for (p = cdesc, pe = cdesc + 100; p < pe; p++) + *p = 0; + + time (&initialtime); + + srand ((unsigned)initialtime); + + lcreat ((char *) 0); /* open buffering for output to terminal */ +} + + + +/* +* lprintf(format,args . . .) printf to the output buffer +* char *format; +* ??? args . . . +* +* Enter with the format string in "format", as per printf() usage +* and any needed arguments following it +* Note: lprintf() only supports %s, %c and %d, with width modifier and left +* or right justification. +* No correct checking for output buffer overflow is done, but flushes +* are done beforehand if needed. +* Returns nothing of value. +*/ +void +lprintf (const char *fmt, ...) +{ + va_list vl; + char buffer[STRING_BUFFER_SIZE]; + const char *p; + + va_start (vl, fmt); + vsprintf (buffer, fmt, vl); + va_end (vl); + + p = buffer; + + while (*p != '\0') + { + lprc (*p); + ++p; + } +} + +/* +* lprint(int-integer) send binary integer to output buffer +* int integer; +* +* +---------+---------+---------+---------+ +* | high | | | low | +* | order | | | order | +* | byte | | | byte | +* +---------+---------+---------+---------+ +* 31 --- 24 23 --- 16 15 --- 8 7 --- 0 +* +* The save order is low order first, to high order (4 bytes total) +* and is written to be system independent. +* No checking for output buffer overflow is done, but flushes if needed! +* Returns nothing of value. +*/ +void +lprint (int x) +{ + + if (lpnt >= lpend) + { + + lflush (); + } + + *lpnt++ = 255 & x; + *lpnt++ = 255 & (x >> 8); + *lpnt++ = 255 & (x >> 16); + *lpnt++ = 255 & (x >> 24); +} + + + +/* +* output one byte to the output buffer +*/ +void +lprc (char ch) +{ + + *lpnt++ = ch; + + if (lpnt >= lpend) + { + + lflush (); + } + lflush(); +} + + + + + + + +/* +* lwrite(buf,len) write a buffer to the output buffer +* char *buf; +* int len; +* +* Enter with the address and number of bytes to write out +* Returns nothing of value +*/ +void +lwrite (char *buf, int len) +{ + char *str; + int num2; + + if (len > 399) /* don't copy data if can just write it */ + { +#ifdef EXTRA + cdesc[BYTESOUT] += len; +#endif + + for (str = buf; len > 0; --len) + lprc (*str++); + } + else + while (len) + { + if (lpnt >= lpend) + lflush (); /* if buffer is full flush it */ + num2 = lpbuf + BUFBIG - lpnt; /* # bytes left in output buffer */ + if (num2 > len) + num2 = len; + str = lpnt; + len -= num2; + while (num2--) + *str++ = *buf++; /* copy in the bytes */ + lpnt = str; + } +} + + +/* +* int lgetc() Read one character from input buffer +* +* Returns 0 if EOF, otherwise the character +*/ +char +lgetc (void) +{ + int i; + + if (ipoint != iepoint) + return (inbuffer[ipoint++]); + if (iepoint != MAXIBUF) + return (0); +#if defined WINDOWS || WINDOWS_VS + if ((i = _read(fd, inbuffer, MAXIBUF)) <= 0) +#endif +#if defined NIX + if ((i = read(fd, inbuffer, MAXIBUF)) <= 0) +#endif + { + if (i != 0) + fprintf (stderr, "error reading from input file\n"); + iepoint = ipoint = 0; + return (0); + } + ipoint = 1; + iepoint = i; + return (*inbuffer); +} + + +/* +* int larint() Read one integer from input buffer +* +* +---------+---------+---------+---------+ +* | high | | | low | +* | order | | | order | +* | byte | | | byte | +* +---------+---------+---------+---------+ +* 31 --- 24 23 --- 16 15 --- 8 7 --- 0 +* +* The save order is low order first, to high order (4 bytes total) +* Returns the int read +*/ +int +larint (void) +{ + int i; + i = 255 & lgetc (); + i |= (255 & lgetc ()) << 8; + i |= (255 & lgetc ()) << 16; + i |= (255 & lgetc ()) << 24; + return (i); +} + + + +/* +* lrfill(address,number) put input bytes into a buffer +* char *address; +* int number; +* +* Reads "number" bytes into the buffer pointed to by "address". +* Returns nothing of value +*/ +void +lrfill (char *adr, int num) +{ + char *pnt; + int num2; + + while (num) + { + if (iepoint == ipoint) + { + if (num > 5) /* fast way */ + { +#if defined WINDOWS || WINDOWS_VS + if (_read(fd, adr, num) != num) +#endif +#if defined NIX + if (read(fd, adr, num) != num) +#endif + fprintf (stderr, "error reading from input file\n"); + num = 0; + } + else + { + *adr++ = lgetc(); + --num; + } + } + else + { + num2 = iepoint - ipoint; /* # of bytes left in the buffer */ + if (num2 > num) + num2 = num; + pnt = inbuffer + ipoint; + num -= num2; + ipoint += num2; + while (num2--) + *adr++ = *pnt++; + } + } +} + + + +/* +* char *lgetw() Get a whitespace ended word from input +* +* Returns pointer to a buffer that contains word. If EOF, returns a NULL +*/ +char * +lgetw (void) +{ + char *lgp, cc; + int n = LINBUFSIZE, quote = 0; + + lgp = lgetwbuf; + do + cc = lgetc (); + while ((cc <= 32) && (cc > '\0')); /* eat whitespace */ + for (;; --n, cc = lgetc ()) + { + if ((cc == '\0') && (lgp == lgetwbuf)) + return (NULL); /* EOF */ + if ((n <= 1) || ((cc <= 32) && (quote == 0))) + { + *lgp = '\0'; + return lgetwbuf; + } + if (cc != '"') + *lgp++ = cc; + else + quote ^= 1; + } +} + + + +/* +* char *lgetl() Function to read in a line ended by newline or EOF +* +* Returns pointer to a buffer that contains the line. If EOF, returns NULL +*/ +char * +lgetl (void) +{ + int i = LINBUFSIZE; + char ch; + char *str = lgetwbuf; + + for (;; --i) + { + *str++ = ch = lgetc (); + if (ch == 0) + { + if (str == lgetwbuf + 1) + return (NULL); /* EOF */ + ot:*str = 0; + return (lgetwbuf); /* line ended by EOF */ + } + if ((ch == '\n') || (i <= 1)) + goto ot; /* line ended by \n */ + } +} + +/* Just for the record, I removed all those _setmodes for *nix + * systems a while ago. No need to declare these + * things to make it work on them, + * these systems just don't require it. ~Gibbon +*/ + +/* +* lcreat(filename) Create a new file for write +* char *filename; +* +* lcreat((char*)0); means to the terminal +* Returns -1 if error, otherwise the file descriptor opened. +* +* Modernised this function and made it cleaner. ~Gibbon +*/ +int +lcreat (char *str) +{ + lpnt = lpbuf; + lpend = lpbuf + BUFBIG; + if (str == NULL) + return (lfd = 1); +#if defined WINDOWS + if ((lfd = _creat(str, _S_IWRITE)) < 0) +#endif +#if defined WINDOWS_VS + if ((lfd = _creat(str, S_IWRITE)) < 0) +#endif +#if defined NIX + if ((lfd = open(str, O_RDWR | O_CREAT, 0666)) < 0) +#endif + { + lfd = 1; + lprintf("error creating file <%s>\n", str); + lflush(); + return(-1); + } +#if defined WINDOWS || WINDOWS_VS + _setmode (lfd, O_BINARY); +#endif + return lfd; +} + + + +/* +* lopen(filename) Open a file for read +* char *filename; +* +* lopen(0) means from the terminal +* Returns -1 if error, otherwise the file descriptor opened. +*/ +int +lopen (char *str) +{ + ipoint = iepoint = MAXIBUF; + + if (str == NULL) + return (fd = 0); +#if defined WINDOWS || WINDOWS_VS + if ((fd = _open(str, 0)) < 0) +#endif +#if defined NIX + if ((fd = open(str, 0)) < 0) +#endif + { + lwclose (); + lfd = 1; + lpnt = lpbuf; + return (-1); + } +#if defined WINDOWS || WINDOWS_VS + _setmode (fd, O_BINARY); +#endif + return fd; +} + + + +/* +* lappend(filename) Open for append to an existing file +* char *filename; +* +* lappend(0) means to the terminal +* Returns -1 if error, otherwise the file descriptor opened. +*/ +int +lappend (char *str) +{ + lpnt = lpbuf; + lpend = lpbuf + BUFBIG; + if (str == NULL) + return (lfd = 1); +#if defined WINDOWS || WINDOWS_VS + if ((lfd = _open(str, 2)) < 0) +#endif +#if defined NIX + if ((lfd = open(str, 2)) < 0) +#endif + { + lfd = 1; + return (-1); + } +#if defined WINDOWS || WINDOWS_VS + _setmode (lfd, O_BINARY); + _lseek (lfd, 0L, 2); /* seek to end of file */ +#endif +#if defined NIX + lseek (lfd, 0L, 2); /* seek to end of file */ +#endif + return lfd; +} + +/* +* lrclose() close the input file +* +* Returns nothing of value. +*/ +void +lrclose (void) +{ + if (fd > 0) + { +#if defined WINDOWS || WINDOWS_VS + _close(fd); +#endif +#if defined NIX + close(fd); +#endif + } +} + + + +/* +* lwclose() close output file flushing if needed +* +* Returns nothing of value. +*/ +void +lwclose (void) +{ + lflush(); + if (lfd > 2) + { +#if defined WINDOWS || WINDOWS_VS + _close(lfd); +#endif +#if defined NIX + close(lfd); +#endif + } +} + +/* +* lprcat(string) append a string to the output buffer +* avoids calls to lprintf (time consuming) +*/ +void +lprcat (char *str) +{ + char *str2; + + if (lpnt >= lpend) + { + lflush(); + } + str2 = lpnt; + + while ((*str2++ = *str++) != '\0') + ; + lpnt = str2 - 1; + lflush(); +} + +/* +* cursor(x,y) Put cursor at specified coordinates staring at [1,1] (termcap) +*/ +void +cursor (int x, int y) +{ + if (lpnt >= lpend) + { + lflush(); + } + *lpnt++ = CURSOR; + *lpnt++ = (char) x; + *lpnt++ = (char) y; +} + +/* +* Routine to position cursor at beginning of 24th line +*/ +void +cursors (void) +{ + cursor(1, 24); +} + +/* +* Warning: ringing the bell is control code 7. Don't use in defines. +* Don't change the order of these defines. +* Also used in helpfiles. Codes used in helpfiles should be \E[1 to \E[7 with +* obvious meanings. +*/ + +/* translated output buffer */ +static char *outbuf = NULL; + +/* +* ANSI control sequences +*/ + +/* clear to end of line */ +static const char CE[] = { 27, '[', 'K', 0 }; + +/* clear to end of display */ +static char *CD = NULL; + +/* clear screen */ +static const char CL[] = { 27, '[', ';', 'H', 27, '[', '2', 'J', 0 }; + +/* cursor motion */ +static const char CM[] = + { 27, '[', '%', 'i', '%', '2', ';', '%', '2', 'H', 0 }; + +/* Removed the 'static' from the AL[] and DL[] as these are unused and static + isn't really needed as the variable itself is never used which + negates the need for a local version which is why you would ever use + static anyway. /rant -Gibbon +*/ + +/* insert line */ +const char AL[] = { 27, '[', 'L', 0 }; + +/* delete line */ +const char DL[] = { 27, '[', 'M', 0 }; + +/* begin standout mode */ +static const char SO[] = { 27, '[', '1', 'm', 0 }; + +/* end standout mode */ +static const char SE[] = { 27, '[', 'm', 0 }; + +/* terminal initialization */ +static const char TI[] = { 27, '[', 'm', 0 }; + +/* terminal end */ +static const char TE[] = { 27, '[', 'm', 0 }; + + +/* +* init_term() Terminal initialization +*/ +void +init_term (void) +{ + /* get memory for decoded output buffer */ + outbuf = malloc (BUFBIG + 16); + if (outbuf == NULL) + { + fprintf (stderr, "Error malloc'ing memory for decoded output buffer\n"); + /* malloc() failure */ + died (-285); + } + ansiterm_init (); +} + +/* +* cl_line(x,y) Clear the whole line indicated by 'y' and leave cursor at [x,y] +*/ +void +cl_line (int x, int y) +{ + cursor (1, y); + *lpnt++ = CL_LINE; + cursor (x, y); +} + +/* +* cl_up(x,y) Clear screen from [x,1] to current position. Leave cursor at [x,y] +*/ +void +cl_up (int x, int y) +{ + int i; + cursor (1, 1); + for (i = 1; i <= y; i++) + { + *lpnt++ = CL_LINE; + *lpnt++ = '\n'; + } + cursor (x, y); +} + +/* +* cl_dn(x,y) Clear screen from [1,y] to end of display. Leave cursor at [x,y] +*/ +void +cl_dn (int x, int y) +{ + int i; + cursor (1, y); + if (CD == NULL) + { + *lpnt++ = CL_LINE; + + for (i = y; i <= 24; i++) + { + *lpnt++ = CL_LINE; + + if (i != 24) + *lpnt++ = '\n'; + } + cursor (x, y); + + } + else + { + *lpnt++ = CL_DOWN; + } + cursor (x, y); +} + +/* +* lstandout(str) Print the argument string in inverse video (standout mode). +*/ +void +lstandout (char *str) +{ + *lpnt++ = ST_START; + while (*str) + { + *lpnt++ = *str++; + } + *lpnt++ = ST_END; +} + +/* +* set_score_output() Called when output should be literally printed. +*/ +void +set_score_output (void) +{ + enable_scroll = -1; +} + +/* +* lflush() Flush the output buffer +* +* Returns nothing of value. +* for termcap version: Flush output in output buffer according to output +* status as indicated by `enable_scroll' +*/ +static int scrline = 18; /* line # for wraparound instead of scrolling if no DL */ + + +void +lflush (void) +{ + int lpoint; + char *str; + static int curx = 0; + static int cury = 0; + + if ((lpoint = lpnt - lpbuf) > 0) + { +#ifdef EXTRA + cdesc[BYTESOUT] += lpoint; +#endif + if (enable_scroll <= -1) + { + flush_buf(); + + /* Catch write errors on save files + */ +#if defined WINDOWS || WINDOWS_VS + if (_write(lfd, lpbuf, lpoint) != lpoint) +#endif +#if defined NIX + if (write(lfd, lpbuf, lpoint) != lpoint) +#endif + { + fprintf(stderr,"Error writing output file\n"); + } + lpnt = lpbuf; /* point back to beginning of buffer */ + return; + } + for (str = lpbuf; str < lpnt; str++) + { + if (*str >= 32) + { + ttputch (*str); + curx++; + } + else + switch (*str) + { + case CLEAR: + tputs (CL); + curx = cury = 0; + break; + + case CL_LINE: + tputs (CE); + break; + + case CL_DOWN: + tputs (CD); + break; + + case ST_START: + tputs (SO); + break; + + case ST_END: + tputs (SE); + break; + + case CURSOR: + curx = *++str - 1; + cury = *++str - 1; + tputs (atgoto (CM, curx, cury)); + break; + + case '\n': + if ((cury == 23) && enable_scroll) + { + + if (++scrline > 23) + { + + scrline = 19; + } + + tputs (atgoto (CM, 0, scrline + 1)); + tputs (CE); + + tputs (atgoto (CM, 0, scrline)); + tputs (CE); + + } + else + { + + ttputch ('\n'); + cury++; + } + + curx = 0; + break; + + case T_INIT: + tputs (TI); + break; + case T_END: + tputs (TE); + break; + default: + ttputch (*str); + curx++; + } + } + } + lpnt = lpbuf; + flush_buf (); /* flush real output buffer now */ +} + +static int io_index = 0; + +/* +* ttputch(ch) Print one character in decoded output buffer. +*/ +static void +ttputch (int c) +{ + + outbuf[io_index++] = (char) c; + + if (io_index >= BUFBIG) + { + + flush_buf (); + } +} + + + +/* +* Outputs string pointed to by cp. Modified using public domain termcap +* routines. ~Gibbon. +*/ +static void +tputs (const char *cp) +{ + if (cp == NULL) + { + } + while (*cp != '\0') + { + ttputch (*cp++); + } +} + + +/* +* flush_buf() Flush buffer with decoded output. +*/ +static void +flush_buf (void) +{ + if (io_index) + { + if (lfd == 1) + { + ansiterm_out (outbuf, io_index); + } + else + { + write(lfd, outbuf, io_index); + } + } + io_index = 0; +} + +/* +* flushall() Function to flush all type-ahead in the input buffer +* +* I've fixed this mess. I don't know who implemented this +* but kbhit is Windows only. I'm guessing they never +* used a BSD or GNU/Linux system or never read a manpage. +* +* I prefer declaring each individually as it is safer +* and also prevent them from being compiled +* when not used by the systems defs. +* +* Nowadays we can do a fflush(NULL) on Unix-like systems. +* +* ~Gibbon +*/ + +#if defined WINDOWS || WINDOWS_VS +void +lflushall (void) +{ + while (kbhit()) + { + _getch(); + } +} +#endif + + +/* +* char *tmcapcnv(sd,ss) Routine to convert VT100 escapes to termcap format +* +* Processes only the \33[#m sequence (converts . files for termcap use +*/ +char * +tmcapcnv (char *sd, char *ss) +{ + int tmstate = 0; /* 0=normal, 1=\33 2=[ 3=# */ + char tmdigit = 0; /* the # in \33[#m */ + + while (*ss) + { + switch (tmstate) + { + case 0: + if (*ss == '\33') + { + tmstate++; + break; + } + ign:*sd++ = *ss; + ign2:tmstate = 0; + break; + case 1: + if (*ss != '[') + goto ign; + tmstate++; + break; + case 2: + if (isdigit (*ss)) + { + tmdigit = *ss - '0'; + tmstate++; + break; + } + if (*ss == 'm') + { + *sd++ = ST_END; + goto ign2; + } + goto ign; + case 3: + if (*ss == 'm') + { + if (tmdigit) + *sd++ = ST_START; + else + *sd++ = ST_END; + goto ign2; + } + default: + goto ign; + }; + ss++; + } + *sd = 0; /* NULL terminator */ + return (sd); +} + +void +enter_name (void) +{ + int i; + + lprcat ("\n\nEnter character name:\n"); + + sncbr (); + + i = 0; + + do + { + char c; + + c = ttgetch (); + + if (c == '\n') + break; + if (c == 8) + { + if (i > 0) + { + --i; + ansiterm_delch (); + } + } + else if (isprint (c)) + { + logname[i] = c; + ++i; + } + + } + while (i < LOGNAMESIZE - 1); + + logname[i] = '\0'; + + scbr (); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c6b8943 --- /dev/null +++ b/src/main.c @@ -0,0 +1,1304 @@ +/* main.c */ +#include + +#include "includes/larn.h" +#include "includes/tok.h" +#include "includes/create.h" +#include "includes/diag.h" +#include "includes/display.h" +#include "includes/fortune.h" +#include "includes/global.h" +#include "includes/help.h" +#include "includes/inventory.h" +#include "includes/io.h" +#include "includes/main.h" +#include "includes/moreobj.h" +#include "includes/movem.h" +#include "includes/object.h" +#include "includes/regen.h" +#include "includes/scores.h" +#include "includes/spells.h" +#include "includes/spheres.h" + + /* needed for hack fix to handle endwin() + not being called after process commandline */ + +#define SCORENAME "data/scorefile.dat" +#define LOGFNAME "data/logfile.log" +#define FORTSNAME "data/forts.txt" +#define PLAYERIDS "data/playerid.txt" +#define DIAGFILE "data/diagfile.txt" +#define SAVEFILE "data/savefile.dat" +#define LEVELSNAME "data/mazefile.txt" + +static void parse (void); + +static void randmonst (void); + +static void run (int); + +static void wield (void); + +static void ydhi (int); +static void ycwi (int); + +static void wear (void); + +static void dropobj (void); + +static int floor_consume (int, char *); + +static void consume (int, char *, int (*)(void)); + +static int whatitem (char *); + +int dropflag = 0; /* if 1 then don't lookforobject() next round */ +int rmst = 80; /* random monster creation counter */ +int nomove = 0; /* if (nomove) then don't count next iteration as a + move */ +static char viewflag = 0; /* if viewflag then we have done a 99 stay here + and don't showcell in the main loop */ +int restorflag = 0; /* 1 means restore has been done */ + +static char cmdhelp[] = "\ +Cmd line format: larn [-sih] [-##]\n\ +-s show the scoreboard\n\ +-i show scoreboard with inventories of dead characters\n\ +-## specify level of difficulty (example: -5)\n\ +-h print this help text\n\ +"; + +signed int save_mode = 0; /* 1 if doing a save game */ + +/* +************ +MAIN PROGRAM +************ +*/ +int +main (int argc, char *argv[]) +{ + int i; + int hard = -1; + + FILE *pFile; + + + /* + * first task is to identify the player + */ + /*init curses ~Gibbon */ + init_term (); /* setup the terminal (find out what type) for termcap */ + scbr (); + /* + * second task is to prepare the pathnames the player will need + */ + + + /* Set up the input and output buffers. + */ + lpbuf = (char *) malloc ((5 * BUFBIG) >> 2); /* output buffer */ + inbuffer = (char *) malloc ((5 * MAXIBUF) >> 2); /* output buffer */ + if ((lpbuf == 0) || (inbuffer == 0)) + died (-285); /* malloc() failure */ + + strcpy (savefilename, SAVEFILE); + strcpy (scorefile, SCORENAME); /* the larn scoreboard filename */ + strcpy (logfile, LOGFNAME); /* larn activity logging filename */ + strcpy (fortfile, FORTSNAME); /* the fortune data file name */ + strcpy (playerids, PLAYERIDS); /* the playerid data file name */ + strcpy (mazefile, LEVELSNAME); + +#ifdef EXTRA + strcpy (diagfile, DIAGFILE); +#endif + + + + /* + * now make scoreboard if it is not there (don't clear) + */ + + pFile = fopen (scorefile, "r"); + if (pFile == 0) /* not there */ + makeboard (); + else + fclose (pFile); + + /* + * now process the command line arguments + */ + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + switch (argv[i][1]) + { + case 's': /* show scoreboard */ + showscores (); + lprcat ("Press any key to exit..."); + ttgetch (); + ansiterm_clean_up (); /* hacky way */ + exit (EXIT_SUCCESS); + + case 'i': /* show all scoreboard */ + showallscores (); + lprcat ("Press any key to exit..."); + ttgetch (); + ansiterm_clean_up (); + exit (EXIT_SUCCESS); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': /* for hardness */ + hard = atoi (&argv[i][1]); + break; + + case 'h': /* print out command line arguments */ + case '?': + ansiterm_clean_up (); + puts (cmdhelp); + exit (EXIT_SUCCESS); + + default: + ansiterm_clean_up (); + printf ("Unknown option <%s>\n", argv[i]); + puts (cmdhelp); + exit (EXIT_SUCCESS); + }; + } + + /* + * He really wants to play, so malloc the memory for the dungeon. + */ + cell = malloc ((sizeof *cell) * (MAXLEVEL + MAXVLEVEL) * MAXX * MAXY); + if (cell == NULL) + { + + /* malloc failure */ + died (-285); + } + + lcreat ((char *) 0); + newgame (); /* set the initial clock */ + + pFile = fopen (savefilename, "r"); + if (pFile != 0) /* restore game if need to */ + { + fclose (pFile); + screen_clear(); + restorflag = 1; + hitflag = 1; + restoregame (savefilename); /* restore last game */ + remove (savefilename); + } + + setupvt100 (); /* setup the terminal special mode */ + if (cdesc[HP] == 0) /* create new game */ + { + predostuff = 1; /* tell signals that we are in the welcome screen */ + welcome (); /* welcome the player to the game */ + + makeplayer (); /* make the character that will play */ + sethard (hard); /* set up the desired difficulty */ + + newcavelevel (0); /* make the dungeon */ + + /* Display their mail if they've just won the previous game + */ + checkmail (); + + } + + lprc (T_INIT); /* Reinit the screen because of welcome and check mail + * having embedded escape sequences.*/ + drawscreen (); /* show the initial dungeon */ + + /* tell the trap functions that they must do a showplayer() from here on */ + predostuff = 2; + + yrepcount = hit2flag = 0; + + /* + * init previous player position to be current position, so we don't + * reveal any stuff on the screen prematurely. + */ + oldx = playerx; + oldy = playery; + /* gtime = -1; */ + + /* MAINLOOP + find objects, move stuff, get commands, regenerate + */ + for (;;) + { + if (dropflag == 0) + { + /* see if there is an object here. + + If in prompt mode, identify and prompt; else + identify, never prompt. + */ + lookforobject (TRUE, FALSE, FALSE); + } + else + { + dropflag = 0; /* don't show it just dropped an item */ + } + + /* handle global activity + update game time, move spheres, move walls, move monsters + all the stuff affected by TIMESTOP and HASTESELF + */ + if (cdesc[TIMESTOP] <= 0) + if (cdesc[HASTESELF] == 0 || (cdesc[HASTESELF] & 1) == 0) + { + gtime++; + movsphere (); + + if (hitflag == 0) + { + if (cdesc[HASTEMONST]) + movemonst (); + movemonst (); + } + } + + /* show stuff around the player + */ + if (viewflag == 0) + showcell (playerx, playery); + else + viewflag = 0; + + if (hit3flag) +#if defined WINDOWS || WINDOWS_VS +lflushall(); +#endif + +#if defined NIX +fflush(NULL); +#endif + hitflag = hit3flag = 0; + bot_linex (); /* update bottom line */ + + /* get commands and make moves + */ + nomove = 1; + while (nomove) + { + if (hit3flag) +#if defined WINDOWS || WINDOWS_VS +lflushall(); +#endif + +#if defined NIX +fflush(NULL); +#endif + nomove = 0; + parse (); + } + regen (); /* regenerate hp and spells */ + if (cdesc[TIMESTOP] == 0) + if (--rmst <= 0) + { + rmst = 120 - (level << 2); + fillmonst (makemonst (level)); + } + } +} + +/* +* subroutine to randomly create monsters if needed +*/ +static void +randmonst (void) +{ + + /* don't make monsters if time is stopped */ + if (cdesc[TIMESTOP]) + { + + return; + } + + if (--rmst <= 0) + { + + rmst = 120 - (level << 2); + + fillmonst (makemonst (level)); + } +} + + + +/* +* parse() +* +* get and execute a command +*/ +static void +parse (void) +{ + int i, j, k, flag; + + for (;;) + { + k = yylex (); + switch (k) /* get the token from the input and switch on it */ + { + case 'h': + moveplayer (4); + return; /* west */ + case 'H': + run (4); + return; /* west */ + case 'l': + moveplayer (2); + return; /* east */ + case 'L': + run (2); + return; /* east */ + case 'j': + moveplayer (1); + return; /* south */ + case 'J': + run (1); + return; /* south */ + case 'k': + moveplayer (3); + return; /* north */ + case 'K': + run (3); + return; /* north */ + case 'u': + moveplayer (5); + return; /* northeast */ + case 'U': + run (5); + return; /* northeast */ + case 'y': + moveplayer (6); + return; /* northwest */ + case 'Y': + run (6); + return; /* northwest */ + case 'n': + moveplayer (7); + return; /* southeast */ + case 'N': + run (7); + return; /* southeast */ + case 'b': + moveplayer (8); + return; /* southwest */ + case 'B': + run (8); + return; /* southwest */ + + case '.': /* stay here */ + if (yrepcount) + viewflag = 1; + return; + + case 'c': + yrepcount = 0; + cast (); + return; /* cast a spell */ + + case 'd': + yrepcount = 0; + if (cdesc[TIMESTOP] == 0) + dropobj (); + return; /* to drop an object */ + + case 'e': + yrepcount = 0; + if (cdesc[TIMESTOP] == 0) + if (!floor_consume (OCOOKIE, "eat")) + consume (OCOOKIE, "eat", showeat); + return; /* to eat a fortune cookie */ + + case 'g': + yrepcount = 0; + cursors (); + lprintf ("\nThe stuff you are carrying presently weighs %d pounds", + (int) packweight ()); + break; + + case 'i': /* inventory */ + yrepcount = 0; + nomove = 1; + showstr (FALSE); + return; + + case 'p': /* pray at an altar */ + yrepcount = 0; + pray_at_altar (); + return; + + case 'q': /* quaff a potion */ + yrepcount = 0; + if (cdesc[TIMESTOP] == 0) + if (!floor_consume (OPOTION, "quaff")) + consume (OPOTION, "quaff", showquaff); + return; + + case 'r': + yrepcount = 0; + if (cdesc[BLINDCOUNT]) + { + cursors (); + lprcat ("\nYou can't read anything when you're blind!"); + } + else if (cdesc[TIMESTOP] == 0) + if (!floor_consume (OSCROLL, "read")) + if (!floor_consume (OBOOK, "read")) + consume (OSCROLL, "read", showread); + return; /* to read a scroll */ + + case 's': + yrepcount = 0; + sit_on_throne (); + return; + + case 't': /* Tidy up at fountain */ + yrepcount = 0; + wash_fountain (); + return; + + case 'v': + yrepcount = 0; + nomove = 1; + cursors (); + lprintf ("\nLarn, Version %d.%d.%d, Diff=%d", (int) VERSION, + (int) SUBVERSION, (int) PATCHLEVEL, (int) cdesc[HARDGAME]); + + if (wizard) + lprcat (" Wizard"); + if (cheat) + lprcat (" Cheater"); + return; + + case 'w': /* wield a weapon */ + yrepcount = 0; + wield (); + return; + + case 'A': + yrepcount = 0; + desecrate_altar (); + return; + + case 'C': /* Close something */ + yrepcount = 0; + close_something (); + return; + + case 'D': /* Drink at fountain */ + yrepcount = 0; + drink_fountain (); + return; + + case '?': + yrepcount=0; + display_help_text(); + nomove = 1; + return; /*give the help screen*/ + + case 'E': /* Enter a building */ + yrepcount = 0; + enter (); + break; + + case 'I': /* list spells and scrolls */ + yrepcount = 0; + seemagic (0); + nomove = 1; + return; + + case 'O': /* Open something */ + yrepcount = 0; + open_something (); + return; + + case 'P': + cursors (); + yrepcount = 0; + nomove = 1; + if (outstanding_taxes > 0) + lprintf ("\nYou presently owe %d gp in taxes.", + (int) outstanding_taxes); + else + lprcat ("\nYou do not owe any taxes."); + return; + + case 'Q': /* quit */ + yrepcount = 0; + quit(); + nomove = 1; + return; + + case 'R': /* remove gems from a throne */ + yrepcount = 0; + remove_gems (); + return; + + case 'S': + /* And do the save. + */ + cursors(); + lprintf("\nSaving to `%s' . . . ", savefilename); + lflush(); + save_mode = 1; + savegame(savefilename); + screen_clear(); + lflush(); + wizard = 1; + died(-257); /* doesn't return */ + break; + + + case 'T': + yrepcount = 0; + cursors (); + if (cdesc[SHIELD] != -1) + { + cdesc[SHIELD] = -1; + lprcat ("\nYour shield is off"); + bottomline (); + } + else if (cdesc[WEAR] != -1) + { + cdesc[WEAR] = -1; + lprcat ("\nYour armor is off"); + bottomline (); + } + else + lprcat ("\nYou aren't wearing anything"); + return; + + case 'W': + yrepcount = 0; + wear (); + return; /* wear armor */ + + case 'Z': + yrepcount = 0; + if (cdesc[LEVEL] > 9) + { + oteleport (1); + return; + } + cursors (); + lprcat + ("\nAs yet, you don't have enough experience to use teleportation"); + return; /* teleport yourself */ + + case ' ': + yrepcount = 0; + nomove = 1; + return; + + case 'L' - 64: + yrepcount = 0; + drawscreen (); + nomove = 1; + return; /* look */ + +#if WIZID +#ifdef EXTRA + case 'A' - 64: + yrepcount = 0; + nomove = 1; + if (wizard) + { + diag (); + return; + } /* create diagnostic file */ + return; +#endif +#endif + + case '<': /* Go up stairs or vol shaft */ + yrepcount = 0; + up_stairs (); + return; + + case '>': /* Go down stairs or vol shaft */ + yrepcount = 0; + down_stairs (); + return; + + case ',': /* pick up an item */ + yrepcount = 0; + /* pickup, don't identify or prompt for action */ + lookforobject (FALSE, TRUE, FALSE); + return; + + case ':': /* look at object */ + yrepcount = 0; + /* identify, don't pick up or prompt for action */ + lookforobject (TRUE, FALSE, FALSE); + nomove = 1; /* assumes look takes no time */ + return; + + case '/': /* identify object/monster */ + specify_object (); + nomove = 1; + yrepcount = 0; + return; + + case '^': /* identify traps */ + flag = yrepcount = 0; + cursors (); + lprc ('\n'); + for (j = playery - 1; j < playery + 2; j++) + { + if (j < 0) + j = 0; + if (j >= MAXY) + break; + for (i = playerx - 1; i < playerx + 2; i++) + { + if (i < 0) + i = 0; + if (i >= MAXX) + break; + switch (item[i][j]) + { + case OTRAPDOOR: + case ODARTRAP: + case OTRAPARROW: + case OTELEPORTER: + case OPIT: + lprcat ("\nIts "); + lprcat (objectname[item[i][j]]); + flag++; + }; + } + } + if (flag == 0) + lprcat ("\nNo traps are visible"); + return; + +#if WIZID + case '_': /* this is the fudge player password for wizard mode */ + yrepcount = 0; + cursors (); + nomove = 1; + wizard = 1; /* disable to easily test win condition */ + scbr (); /* system("stty -echo cbreak"); */ + for (i = 0; i < 6; i++) + { + cdesc[i] = 70; + } + iven[0] = iven[1] = 0; + /* give the ring a little boost. ~Gibbon */ + take (OPROTRING, 51); + /* lets nerf it a little bit. + * ~Gibbon */ + take (OGREATSWORD, 24); + cdesc[WIELD] = 1; + cdesc[GREATSWORDDEATH] = 1; + raiseexperience (6000000L); + cdesc[AWARENESS] += 25000; + { + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + know[j][i] = KNOWALL; + for (i = 0; i < SPNUM; i++) + spelknow[i] = 1; + for (i = 0; i < MAXSCROLL; i++) + scrollname[i][0] = ' '; + for (i = 0; i < MAXPOTION; i++) + potionname[i][0] = ' '; + } + for (i = 0; i < MAXSCROLL; i++) + /* no null items */ + if (strlen (scrollname[i]) > 2) + { + item[i][0] = OSCROLL; + iarg[i][0] = i; + } + for (i = MAXX - 1; i > MAXX - 1 - MAXPOTION; i--) + /* no null items */ + if (strlen (potionname[i - MAXX + MAXPOTION]) > 2) + { + item[i][0] = OPOTION; + iarg[i][0] = i - MAXX + MAXPOTION; + } + for (i = 1; i < MAXY; i++) + { + item[0][i] = i; + iarg[0][i] = 0; + } + for (i = MAXY; i < MAXY + MAXX; i++) + { + item[i - MAXY][MAXY - 1] = i; + iarg[i - MAXY][MAXY - 1] = 0; + } + for (i = MAXX + MAXY; i < MAXOBJECT; i++) + { + item[MAXX - 1][i - MAXX - MAXY] = i; + iarg[MAXX - 1][i - MAXX - MAXY] = 0; + } + cdesc[GOLD] += 250000; + drawscreen (); + return; +#endif + + }; + } +} + + + +void +parse2 (void) +{ + + /* move the monsters */ + if (cdesc[HASTEMONST]) + { + + movemonst (); + } + + movemonst (); + + randmonst (); + + regen (); +} + + + +static void +run (int dir) +{ + int i; + + i = 1; + + while (i) + { + + i = moveplayer (dir); + + if (i > 0) + { + + if (cdesc[HASTEMONST]) + { + + movemonst (); + } + + movemonst (); + randmonst (); + regen (); + } + + if (hitflag) + { + + i = 0; + } + + if (i != 0) + { + + showcell (playerx, playery); + } + gtime++; + } +} + + + +/* +* function to wield a weapon +*/ +static void +wield (void) +{ + int i; + + for (;;) + { + + i = whatitem ("wield (- for nothing)"); + if (i == '\33') + return; + + + if (i != '.') + { + + if (i == '*') + { + + i = showwield (); + cursors (); + } + + if (i == '-') + { + + cdesc[WIELD] = -1; + bottomline (); + + return; + } + + if (!i || i == '.') + { + + continue; + } + + if (iven[i - 'a'] == 0) + { + + ydhi (i); + return; + + } + else if (iven[i - 'a'] == OPOTION) + { + + ycwi (i); + return; + + } + else if (iven[i - 'a'] == OSCROLL) + { + + ycwi (i); + return; + + } + else if (cdesc[SHIELD] != -1 && iven[i - 'a'] == O2SWORD) + { + + lprcat ("\nBut one arm is busy with your shield!"); + return; + + } + else if (cdesc[SHIELD] != -1 && iven[i - 'a'] == OHSWORD) + { + + lprcat ("\nA longsword of Hymie cannot be used while a shield is equipped!"); + return; + + } + else + { + + cdesc[WIELD] = i - 'a'; + + if (iven[i - 'a'] == OGREATSWORD) + { + + cdesc[GREATSWORDDEATH] = 1; + + } + else + { + + cdesc[GREATSWORDDEATH] = 0; + } + + bottomline (); + return; + } + } + } +} + + + +/* +* common routine to say you don't have an item +*/ +static void +ydhi (int x) +{ + + cursors (); + + lprintf ("\nYou don't have item %c!", x); +} + + + +/* +* common routine to say you can't wield an item +*/ +static void +ycwi (int x) +{ + cursors (); + + lprintf ("\nYou can't wield item %c!", x); +} + + + +/* +function to wear armor +*/ +static void +wear (void) +{ + int i; + + for (;;) + { + if ((i = whatitem ("wear")) == '\33') + return; + if (i != '.' && i != '-') + { + if (i == '*') + { + i = showwear (); + cursors (); + } + if (i && i != '.') + switch (iven[i - 'a']) + { + case 0: + ydhi (i); + return; + case OLEATHER: + case OCHAIN: + case OPLATE: + case ORING: + case OSPLINT: + case OPLATEARMOR: + case OSTUDLEATHER: + case OSSPLATE: + if (cdesc[WEAR] != -1) + { + lprcat ("\nYou're already wearing some armor"); + return; + } + cdesc[WEAR] = i - 'a'; + bottomline (); + return; + case OSHIELD: + if (cdesc[SHIELD] != -1) + { + lprcat ("\nYou are already wearing a shield"); + return; + } + if (iven[cdesc[WIELD]] == O2SWORD) + { + lprcat + ("\nYour hands are busy with the two handed sword!"); + return; + } + if (iven[cdesc[WIELD]] == OHSWORD) + { + lprcat("\nYou are holding a longsword of Hymie!"); + return; + } + cdesc[SHIELD] = i - 'a'; + bottomline (); + return; + default: + lprcat ("\nYou can't wear that!"); + }; + } + } +} + + + + +/* +function to drop an object +*/ +static void +dropobj (void) +{ + int i; + int *p; + int amt; + + p = &item[playerx][playery]; + for (;;) + { + if ((i = whatitem ("drop")) == '\33') + return; + if (i == '*') + { + i = showstr (TRUE); + cursors (); + } + if (i != '-') + { + if (i == '.') /* drop some gold */ + { + if (*p) + { + lprintf ("\nThere's something here already: %s", + objectname[item[playerx][playery]]); + dropflag = 1; + return; + } + lprcat ("\n\n"); + cl_dn (1, 23); + lprcat ("How much gold do you drop? "); + if ((amt = readnum ((int) cdesc[GOLD])) == 0) + return; + if (amt > cdesc[GOLD]) + { + lprcat ("\n"); + lprcat ("You don't have that much!"); + return; + } + if (amt <= 32767) + { + *p = OGOLDPILE; + i = amt; + } + else if (amt <= 327670L) + { + *p = ODGOLD; + i = amt / 10; + amt = 10L * i; + } + else if (amt <= 3276700L) + { + *p = OMAXGOLD; + i = amt / 100; + amt = 100L * i; + } + else if (amt <= 32767000L) + { + *p = OKGOLD; + i = amt / 1000; + amt = 1000L * i; + } + else + { + *p = OKGOLD; + i = 32767; + amt = 32767000L; + } + cdesc[GOLD] -= amt; + + lprintf ("\nYou drop %d gold pieces", (int) amt); + + iarg[playerx][playery] = i; + bottomgold (); + know[playerx][playery] = 0; + dropflag = 1; + return; + } + if (i) + { + drop_object (i - 'a'); + return; + } + } + } +} + + +static int +floor_consume (int search_item, char *cons_verb) +{ + int i; + char tempc; + + cursors (); + i = item[playerx][playery]; + + /* item not there, quit + */ + if (i != search_item) + return (0); + + /* item there. does the player want to consume it? + */ + lprintf ("\nThere is %s", objectname[i]); + if (i == OSCROLL) + if (scrollname[iarg[playerx][playery]][0]) + lprintf (" of%s", scrollname[iarg[playerx][playery]]); + if (i == OPOTION) + if (potionname[iarg[playerx][playery]][0]) + lprintf (" of%s", potionname[iarg[playerx][playery]]); + lprintf (" here. Do you want to %s it?", cons_verb); + + if ((tempc = getyn ()) == 'n') + return (0); /* item there, not consumed */ + else if (tempc != 'y') + { + lprcat (" aborted"); + return (-1); /* abort */ + } + + /* consume the item. + */ + switch (i) + { + case OCOOKIE: + outfortune (); + forget (); + break; + case OBOOK: + readbook (iarg[playerx][playery]); + forget (); + break; + case OPOTION: + quaffpotion (iarg[playerx][playery], 1); + forget (); + break; + case OSCROLL: + /* scrolls are tricky because of teleport. + */ + i = iarg[playerx][playery]; + know[playerx][playery] = 0; + item[playerx][playery] = iarg[playerx][playery] = 0; + read_scroll (i); + break; + } + return (1); +} + + + +static void +consume (int search_item, char *prompt, int (*showfunc) (void)) +{ + int i; + + for (;;) + { + if ((i = whatitem (prompt)) == '\33') + return; + if (i != '.' && i != '-') + { + if (i == '*') + { + i = showfunc (); + cursors (); + } + if (i && i != '.') + { + switch (iven[i - 'a']) + { + case OSCROLL: + if (search_item != OSCROLL) + { + lprintf ("\nYou can't %s that.", prompt); + return; + } + read_scroll (ivenarg[i - 'a']); + break; + case OBOOK: + if (search_item != OSCROLL) + { + lprintf ("\nYou can't %s that.", prompt); + return; + } + readbook (ivenarg[i - 'a']); + break; + case OCOOKIE: + if (search_item != OCOOKIE) + { + lprintf ("\nYou can't %s that.", prompt); + return; + } + outfortune (); + break; + case OPOTION: + if (search_item != OPOTION) + { + lprintf ("\nYou can't %s that.", prompt); + return; + } + quaffpotion (ivenarg[i - 'a'], TRUE); + break; + case 0: + ydhi (i); + return; + default: + lprintf ("\nYou can't %s that.", prompt); + return; + } + iven[i - 'a'] = 0; + return; + } + } + } +} + + + +/* +function to ask what player wants to do +*/ +static int +whatitem (char *str) +{ + int i = 0; + + cursors (); + lprintf ("\nWhat do you want to %s [* for all] ? ", str); + while (i > 'z' + || (i < 'a' && i != '-' && i != '*' && i != '\33' && i != '.')) + i = ttgetch (); + if (i == '\33') + lprcat (" aborted"); + + return (i); +} + + + + +/* +subroutine to get a number from the player +and allow * to mean return amt, else return the number entered +*/ +int +readnum (int mx) +{ + int i; + int amt = 0; + + sncbr (); + /* allow him to say * for all gold + */ + if ((i = ttgetch ()) == '*') + amt = mx; + else + /* read chars into buffer, deleting when requested */ + while (i != '\n') + { + if (i == '\033') + { + scbr (); + lprcat (" aborted"); + return (0); + } + if ((i <= '9') && (i >= '0') && (amt < 999999999)) + amt = amt * 10 + i - '0'; + if ((i == '\010') || (i == '\177')) + amt = (int) (amt / 10); + i = ttgetch (); + } + scbr (); + return (amt); +} diff --git a/src/monster.c b/src/monster.c new file mode 100644 index 0000000..fef9c7f --- /dev/null +++ b/src/monster.c @@ -0,0 +1,980 @@ +/* +* monster.c +* +* createmonster(monstno) Function to create a monster next to the player +* int monstno; +* +* int cgood(x,y,itm,monst) Function to check location for emptiness +* int x,y,itm,monst; +* +* createitem(it,arg) Routine to place an item next to the player +* int it,arg; +* +* vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds +* int *x,*y; +* +* hitmonster(x,y) Function to hit a monster at the designated coordinates +* int x,y; +* +* hitm(x,y,amt) Function to just hit a monster at a given coordinates +* int x,y,amt; +* +* hitplayer(x,y) Function for the monster to hit the player from (x,y) +* int x,y; +* +* dropsomething(monst) Function to create an object when a monster dies +* int monst; +* +* dropgold(amount) Function to drop some gold around player +* int amount; +* +* something(level) Function to create a random item around player +* int level; +* +* newobject(lev,i) Routine to return a randomly selected new object +* int lev,*i; +* +* spattack(atckno,xx,yy) Function to process special attacks from monsters +* int atckno,xx,yy; +* +* checkloss(x) Routine to subtract hp from user and flag bottomline display +* int x; +* +*/ +#include +#include +#include +#include "includes/ansiterm.h" +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include +#include "includes/display.h" +#include "includes/global.h" +#include "includes/inventory.h" +#include "includes/io.h" +#include "includes/monster.h" +#include "includes/spells.h" +#include "includes/sysdep.h" + +static int cgood (int, int, int, int); + +static void dropsomething (int); + +static int spattack (int, int, int); + +/* +* createmonster(monstno) Function to create a monster next to the player +* int monstno; +* +* Enter with the monster number (1 to MAXMONST+8) +* Returns no value. +*/ +void +createmonster (int mon) +{ + int x, y, k, i; + + if (mon < 1 || mon > MAXMONST + 8) + { /* check for monster number out of bounds */ + lprintf ("\ncan't createmonst(%d)\n", mon); + nap (3000); + return; + } + while (monster[mon].genocided && mon < MAXMONST) + mon++; /* skip genocided */ + for (k = rnd (8), i = -8; i < 0; i++, k++) + { + /* choose rnd direction, then try all */ + if (k > 8) + k = 1; /* wraparound the diroff arrays */ + x = playerx + diroffx[k]; + y = playery + diroffy[k]; + if (cgood (x, y, 0, 1)) + { /* if we can create here */ + mitem[x][y] = mon; + hitp[x][y] = monster[mon].hitpoints; + stealth[x][y] = 0; + know[x][y] &= ~KNOWHERE; + switch (mon) + { + case ROTHE: + case POLTERGEIST: + case VAMPIRE: + stealth[x][y] = 1; + }; + return; + } + } +} + + + +/* +* int cgood(x,y,itm,monst) Function to check location for emptiness +* int x,y,itm,monst; +* +* Routine to return TRUE if a location does not have itm or monst there +* returns FALSE (0) otherwise +* Enter with itm or monst TRUE or FALSE if checking it +* Example: if itm==TRUE check for no item at this location +* if monst==TRUE check for no monster at this location +* This routine will return FALSE if at a wall,door or the dungeon exit +* on level 1 +*/ +static int +cgood (int x, int y, int itm, int monst) +{ + + /* + * cannot create either monster or item if: + * - out of bounds + * - wall + * - closed door + * - dungeon entrance + */ + if (((y < 0) || (y > MAXY - 1) || (x < 0) || (x > MAXX - 1)) || + (item[x][y] == OWALL) || + (item[x][y] == OCLOSEDDOOR) || + ((level == 1) && (x == 33) && (y == MAXY - 1))) + { + return FALSE; + } + + /* if checking for an item, return False if one there already */ + if (itm && item[x][y]) + { + return FALSE; + } + + /* + * if checking for a monster, return False if one there already _or_ + * there is a pit/trap there. + */ + if (monst) + { + if (mitem[x][y]) + { + return FALSE; + } + /* + * note: not invisible traps, since monsters are + * not affected by them. + */ + switch (item[x][y]) + { + case OPIT: + case OANNIHILATION: + case OTELEPORTER: + case OTRAPARROW: + case ODARTRAP: + case OTRAPDOOR: + return FALSE; + } + } + + /* cgood! */ + return TRUE; +} + + + +/* +* createitem(it,arg) Routine to place an item next to the player +* int it,arg; +* +* Enter with the item number and its argument (iven[], ivenarg[]) +* Returns no value, thus we don't know about createitem() failures. +*/ +void +createitem (int it, int arg) +{ + int x, y, k, i; + + if (it >= MAXOBJ) + return; /* no such object */ + for (k = rnd (8), i = -8; i < 0; i++, k++) + { + /* choose rnd direction, then try all */ + if (k > 8) + k = 1; /* wraparound the diroff arrays */ + x = playerx + diroffx[k]; + y = playery + diroffy[k]; + if (cgood (x, y, 1, 0)) + { /* if we can create here */ + item[x][y] = it; + know[x][y] = 0; + iarg[x][y] = arg; + return; + } + } +} + + + +/* +* vxy(x,y) Routine to verify/fix coordinates for being within bounds +* int *x,*y; +* +* Function to verify x & y are within the bounds for a level +* If *x or *y is not within the absolute bounds for a level, fix them so that +* they are on the level. +* Returns TRUE if it was out of bounds, and the *x & *y in the calling +* routine are affected. +*/ +int +vxy (int *x, int *y) +{ + int flag = 0; + + if (*x < 0) + { + *x = 0; + flag++; + } + if (*y < 0) + { + *y = 0; + flag++; + } + if (*x >= MAXX) + { + *x = MAXX - 1; + flag++; + } + if (*y >= MAXY) + { + *y = MAXY - 1; + flag++; + } + return (flag); +} + + + +/* +* hitmonster(x,y) Function to hit a monster at the designated coordinates +* int x,y; +* +* This routine is used for a bash & slash type attack on a monster +* Enter with the coordinates of the monster in (x,y). +* Returns no value. +*/ +void +hitmonster (int x, int y) +{ + int tmp, monst, flag, damag = 0; + + if (cdesc[TIMESTOP]) + return; /* not if time stopped */ + vxy (&x, &y); /* verify coordinates are within range */ + if ((monst = mitem[x][y]) == 0) + return; + hit3flag = 1; + ifblind (x, y); + tmp = monster[monst].armorclass + cdesc[LEVEL] + + cdesc[DEXTERITY] + cdesc[WCLASS] / 4 - 12; + cursors (); + if ((rnd (20) < tmp - cdesc[HARDGAME]) || (rnd (71) < 5)) /* need at least random chance to hit */ + { + lprcat ("\nYou hit"); + flag = 1; + damag = fullhit (1); + if (damag < 9999) + damag = rnd (damag) + 1; + } + else + { + lprcat ("\nYou missed"); + flag = 0; + } + lprcat (" the "); + attron(COLOR_PAIR(2)); + lprcat (lastmonst); + attroff(COLOR_PAIR(2)); + if (flag) /* if the monster was hit */ + if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) + || (monst == CUBE)) + if (cdesc[WIELD] >= 0) + if (ivenarg[cdesc[WIELD]] > -10) + { + lprintf ("\nYour weapon is dulled by the %s", lastmonst); + --ivenarg[cdesc[WIELD]]; + + /* fix for dulled rings of strength, cleverness, dexterity bug. */ + switch (iven[cdesc[WIELD]]) + { + case ODEXRING: + cdesc[DEXTERITY]--; + break; + case OSTRRING: + cdesc[STREXTRA]--; + break; + case OCLEVERRING: + cdesc[INTELLIGENCE]--; + break; + } + /* */ + } + if (flag) + hitm (x, y, damag); + if (monst == VAMPIRE) + if (hitp[x][y] < 25) + { /* vampire turns into bat */ + mitem[x][y] = BAT; + know[x][y] = 0; + } +} + + + +/* +* hitm(x,y,amt) Function to just hit a monster at a given coordinates +* int x,y,amt; +* +* Returns the number of hitpoints the monster absorbed +* This routine is used to specifically damage a monster at a location (x,y) +* Called by hitmonster(x,y) +*/ +int +hitm (int x, int y, int amt) +{ + int monst; + int hpoints, amt2; + + vxy (&x, &y); /* verify coordinates are within range */ + amt2 = amt; /* save initial damage so we can return it */ + monst = mitem[x][y]; + if (cdesc[HALFDAM]) + amt >>= 1; /* if half damage curse adjust damage points */ + if (amt <= 0) + amt2 = amt = 1; + lasthx = x; + lasthy = y; + stealth[x][y] = 1; /* make sure hitting monst breaks stealth condition */ + cdesc[HOLDMONST] = 0; /* hit a monster breaks hold monster spell */ + switch (monst) + { /* if a dragon and orb(s) of dragon slaying */ + case WHITEDRAGON: + case REDDRAGON: + case GREENDRAGON: + case BRONZEDRAGON: + case PLATINUMDRAGON: + case SILVERDRAGON: + amt *= 1 + (cdesc[SLAYING] << 1); + break; + } + /* invincible monster fix is here */ + if (hitp[x][y] > monster[monst].hitpoints) + hitp[x][y] = monster[monst].hitpoints; + if ((hpoints = hitp[x][y]) <= amt) + { +#ifdef EXTRA + cdesc[MONSTKILLED]++; +#endif + lprintf ("\nThe"); + attron(COLOR_PAIR(2)); + lprintf(" %s ",lastmonst); + attroff(COLOR_PAIR(2)); + lprintf("died!\n"); + raiseexperience (monster[monst].experience); + amt = monster[monst].gold; + if (amt > 0) + dropgold (rnd (amt) + amt); + dropsomething (monst); + disappear (x, y); + bottomline (); + return (hpoints); + } + hitp[x][y] = hpoints - amt; + return (amt2); +} + + +/* +* hitplayer(x,y) Function for the monster to hit the player from (x,y) +* int x,y; +* +* Function for the monster to hit the player with monster at location x,y +* Returns nothing of value. +*/ +void +hitplayer (int x, int y) +{ + int dam, tmp, mster, bias; + + vxy (&x, &y); /* verify coordinates are within range */ + lastnum = mster = mitem[x][y]; + /* spirit naga's and poltergeist's do nothing if scarab of negate spirit */ + if (cdesc[NEGATESPIRIT] || cdesc[SPIRITPRO]) + if ((mster == POLTERGEIST) || (mster == SPIRITNAGA)) + return; + /* if undead and cube of undead control */ + if (cdesc[CUBEofUNDEAD] || cdesc[UNDEADPRO]) + if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE)) + return; + if ((know[x][y] & KNOWHERE) == 0) + show1cell (x, y); + bias = (cdesc[HARDGAME]) + 1; + hitflag = hit2flag = hit3flag = 1; + yrepcount = 0; + cursors (); + ifblind (x, y); + if (cdesc[INVISIBILITY]) + if (rnd (33) < 20) + { + lprintf ("\nThe"); + attron(COLOR_PAIR(2)); + lprintf(" %s ",lastmonst); + attroff(COLOR_PAIR(2)); + lprintf("misses wildly\n"); + return; + } + if (cdesc[CHARMCOUNT]) + if (rnd (30) + 5 * monster[mster].level - cdesc[CHARISMA] < 30) + { + lprintf ("\nThe"); + attron(COLOR_PAIR(2)); + lprintf(" %s ",lastmonst); + attroff(COLOR_PAIR(2)); + lprintf("is awestruck at your magnificence!\n"); + return; + } + if (mster == BAT) + dam = 1; + else + { + dam = monster[mster].damage; + dam += rnd ((int) ((dam < 1) ? 1 : dam)) + monster[mster].level; + } + tmp = 0; + if (monster[mster].attack > 0) + if (((dam + bias + 8) > cdesc[AC]) + || (rnd ((int) ((cdesc[AC] > 0) ? cdesc[AC] : 1)) == 1)) + { + if (spattack (monster[mster].attack, x, y)) + { +#if defined WINDOWS || WINDOWS_VS +lflushall(); +#endif + +#if defined NIX +fflush(NULL); +#endif + return; + } + tmp = 1; + bias -= 2; + cursors (); + } + if (((dam + bias) > cdesc[AC]) + || (rnd ((int) ((cdesc[AC] > 0) ? cdesc[AC] : 1)) == 1)) + { + lprintf ("\nThe"); + attron(COLOR_PAIR(2)); + lprintf(" %s ",lastmonst); + attroff(COLOR_PAIR(2)); + lprintf("hit you"); + tmp = 1; + if ((dam -= cdesc[AC]) < 0) + dam = 0; + if (dam > 0) + { + losehp (dam); + bottomhp (); +#if defined WINDOWS || WINDOWS_VS +lflushall(); +#endif + +#if defined NIX +fflush(NULL); +#endif + } + } + if (tmp == 0) + { + lprintf("\nThe"); + attron(COLOR_PAIR(2)); + lprintf(" %s ",lastmonst); + attroff(COLOR_PAIR(2)); + lprintf("missed"); + } +} + +/* +* dropsomething(monst) Function to create an object when a monster dies +* int monst; +* +* Function to create an object near the player when certain monsters are killed +* Enter with the monster number +* Returns nothing of value. +*/ +static void +dropsomething (int monst) +{ + + switch (monst) + { + case ORC: + case NYMPH: + case ELF: + case TROGLODYTE: + case TROLL: + case ROTHE: + case VIOLETFUNGI: + case PLATINUMDRAGON: + case GNOMEKING: + case REDDRAGON: + something (level); + return; + + case LEPRECHAUN: + if (rnd (101) >= 75) + creategem (); + if (rnd (5) == 1) + dropsomething (LEPRECHAUN); + return; + } +} + + + + +/* +* dropgold(amount) Function to drop some gold around player +* int amount; +* +* Enter with the number of gold pieces to drop +* Returns nothing of value. +*/ +void +dropgold (int amount) +{ + + if (amount > 250) + createitem (OMAXGOLD, amount / 100); + else + createitem (OGOLDPILE, amount); +} + + + +/* +* something(level) Function to create a random item around player +* int level; +* +* Function to create an item from a designed probability around player +* Enter with the cave level on which something is to be dropped +* Returns nothing of value. +*/ +void +something (int lv) +{ + int j, i; + + if (lv < 0 || lv > MAXLEVEL + MAXVLEVEL) + return; /* correct level? */ + if (rnd (101) < 8) + something (lv); /* possibly more than one item */ + j = newobject (lv, &i); + createitem (j, i); +} + + + +/* +* newobject(lev,i) Routine to return a randomly selected new object +* int lev,*i; +* +* Routine to return a randomly selected object to be created +* Returns the object number created, and sets *i for its argument +* Enter with the cave level and a pointer to the items arg +*/ +static int nobjtab[] = { 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, + OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE, + OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER, + OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR, + OBELT, ORING, OSTUDLEATHER, OSHIELD, OCOOKIE, OCHAIN, OBATTLEAXE, + OSPLINT, O2SWORD, OHSWORD, OCLEVERRING, OPLATE, OLONGSWORD +}; + + +int +newobject (int lev, int *i) +{ + int tmp = 33, j, hacktmp = 0; + + if (level < 0 || level > MAXLEVEL + MAXVLEVEL) + return (0); /* correct level? */ + if (lev > 6) + tmp = 41; + else if (lev > 4) + tmp = 39; + j = nobjtab[tmp = rnd (tmp)]; /* the object type */ + switch (tmp) + { + case 1: + case 2: + case 3: + case 4: /* scroll */ + *i = newscroll (); + break; + case 5: + case 6: + case 7: + case 8: /* potion */ + *i = newpotion (); + break; + case 9: + case 10: + case 11: + case 12: /* gold */ + *i = rnd ((lev + 1) * 10) + lev * 10 + 10; + break; + case 13: + case 14: + case 15: + case 16: /* book */ + *i = lev; + break; + case 17: + case 18: + case 19: /* dagger */ + hacktmp = (*i = newdagger ()); + if (!hacktmp) + return (0); + break; + case 20: + case 21: + case 22: /* leather armor */ + hacktmp = (*i = newleather ()); + if (!hacktmp) + return (0); + break; + case 23: + case 32: + case 38: /* regen ring, shield, 2-hand sword */ + *i = rund (lev / 3 + 1); + break; + case 24: + case 26: /* prot ring, dexterity ring */ + *i = rnd (lev / 4 + 1); + break; + case 25: /* energy ring */ + *i = rund (lev / 4 + 1); + break; + case 27: + case 39: /* strength ring, cleverness ring */ + *i = rnd (lev / 2 + 1); + break; + case 30: + case 34: /* ring mail, flail */ + *i = rund (lev / 2 + 1); + break; + case 28: + case 36: /* spear, battleaxe */ + *i = rund (lev / 3 + 1); + if (*i == 0) + return (0); + break; + case 29: + case 31: + case 37: /* belt, studded leather, splint */ + *i = rund (lev / 2 + 1); + if (*i == 0) + return (0); + break; + case 33: /* fortune cookie */ + *i = 0; + break; + case 35: /* chain mail */ + *i = newchain (); + break; + case 40: /* plate mail */ + *i = newplate (); + break; + case 41: /* longsword */ + *i = newsword (); + break; + } + return (j); +} + + + +/* +* spattack(atckno,xx,yy) Function to process special attacks from monsters +* int atckno,xx,yy; +* +* Enter with the special attack number, and the coordinates (xx,yy) +* of the monster that is special attacking +* Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise +* +* atckno monster effect +* --------------------------------------------------- +* 0 none +* 1 rust monster eat armor +* 2 hell hound breathe light fire +* 3 dragon breathe fire +* 4 giant centipede weakening sing +* 5 white dragon cold breath +* 6 wraith drain level +* 7 waterlord water gusher +* 8 leprechaun steal gold +* 9 disenchantress disenchant weapon or armor +* 10 ice lizard hits with barbed tail +* 11 umber hulk confusion +* 12 spirit naga cast spells taken from special attacks +* 13 platinum dragon psionics +* 14 nymph steal objects +* 15 bugbear bite +* 16 osequip bite +* +* char rustarm[ARMORTYPES][2]; +* special array for maximum rust damage to armor from rustmonster +* format is: { armor type , minimum attribute +*/ +#define ARMORTYPES 6 +static int rustarm[ARMORTYPES][2] = { + + {OSTUDLEATHER, -2}, + {ORING, -4}, + {OCHAIN, -5}, + {OSPLINT, -6}, + {OPLATE, -8}, + {OPLATEARMOR, -9} + +}; + + + +static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 }; + +static int +spattack (int x, int xx, int yy) +{ + int i, j = 0, k, m; + char *p = 0; + + if (cdesc[CANCELLATION]) + return (0); + vxy (&xx, &yy); /* verify x & y coordinates */ + switch (x) + { + case 1: /* rust your armor, j=1 when rusting has occurred */ + m = k = cdesc[WEAR]; + + i = cdesc[SHIELD]; + + if (i != -1) + { + + if (--ivenarg[i] < -1) + { + ivenarg[i] = -1; + } + else + { + j = 1; + } + } + + if ((j == 0) && (k != -1)) + { + m = iven[k]; + for (i = 0; i < ARMORTYPES; i++) + if (m == rustarm[i][0]) /* find his armor in table */ + { + if (--ivenarg[k] < rustarm[i][1]) + ivenarg[k] = rustarm[i][1]; + else + j = 1; + break; + } + } + if (j == 0) /* if rusting did not occur */ + switch (m) + { + case OLEATHER: + p = "\nThe %s hit you -- Your lucky you have leather on"; + break; + case OSSPLATE: + p = + "\nThe %s hit you -- Your fortunate to have stainless steel armor!"; + break; + } + else + { + p = "\nThe %s hit you -- your armor feels weaker"; + } + break; + + case 2: + i = rnd (15) + 8 - cdesc[AC]; + spout:p = "\nThe %s breathes fire at you!"; + if (cdesc[FIRERESISTANCE]) + p = "\nThe %s's flame doesn't phase you!"; + else + spout2:if (p) + { + lprintf (p, lastmonst); + } + checkloss (i); + return (0); + + case 3: + i = rnd (20) + 25 - cdesc[AC]; + goto spout; + + case 4: + if (cdesc[STRENGTH] > 3) + { + p = "\nThe %s stung you! You feel weaker"; + --cdesc[STRENGTH]; + } + else + p = "\nThe %s stung you!"; + break; + + case 5: + p = "\nThe %s blasts you with his cold breath"; + i = rnd (15) + 18 - cdesc[AC]; + goto spout2; + + case 6: + lprintf ("\nThe "); + attron(COLOR_PAIR(2)); + lprintf("%s",lastmonst); + attroff(COLOR_PAIR(2)); + lprintf(" drains you of your life energy!"); + loselevel (); + return (0); + + case 7: + p = "\nThe %s got you with a gusher!"; + i = rnd (15) + 25 - cdesc[AC]; + goto spout2; + + case 8: + if (cdesc[NOTHEFT]) + return (0); /* he has a device of no theft */ + if (cdesc[GOLD]) + { + p = "\nThe %s hit you -- Your purse feels lighter"; + if (cdesc[GOLD] > 32767) + cdesc[GOLD] >>= 1; + else + cdesc[GOLD] -= rnd ((int) (1 + (cdesc[GOLD] >> 1))); + if (cdesc[GOLD] < 0) + cdesc[GOLD] = 0; + } + else + p = "\nThe %s couldn't find any gold to steal"; + lprintf (p, lastmonst); + disappear (xx, yy); + bottomgold (); + return (1); + + case 9: + for (j = 50;;) /* disenchant */ + { + i = rund (26); + m = iven[i]; /* randomly select item */ + if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) + { + if ((ivenarg[i] -= 3) < 0) + ivenarg[i] = 0; + lprintf ("\nThe "); + attron(COLOR_PAIR(2)); + lprintf("%s ",lastmonst); + attroff(COLOR_PAIR(2)); + lprintf("hits you -- you feel a sense of loss"); + show3 (i); + bottomline (); + return (0); + } + if (--j <= 0) + { + p = "\nThe %s nearly misses"; + break; + } + break; + } + break; + + case 10: + p = "\nThe %s hit you with his barbed tail"; + i = rnd (25) - cdesc[AC]; + goto spout2; + + case 11: + p = "\nThe %s has confused you"; + cdesc[CONFUSE] += 10 + rnd (10); + break; + + case 12: /* performs any number of other special attacks */ + return (spattack (spsel[rund (10)], xx, yy)); + + case 13: + p = "\nThe %s flattens you with his psionics!"; + i = rnd (15) + 30 - cdesc[AC]; + goto spout2; + + case 14: + if (cdesc[NOTHEFT]) + return (0); /* he has device of no theft */ + if (emptyhanded () == 1) + { + p = "\nThe %s couldn't find anything to steal"; + break; + } + lprintf ("\nThe"); + attron(COLOR_PAIR(2)); + lprintf(" %s ",lastmonst); + attroff(COLOR_PAIR(2)); + lprintf("picks your pocket and takes:"); + if (stealsomething () == 0) + lprcat (" nothing"); + disappear (xx, yy); + bottomline (); + return (1); + + case 15: + i = rnd (10) + 5 - cdesc[AC]; + spout3:p = "\nThe %s bit you!"; + goto spout2; + + case 16: + i = rnd (15) + 10 - cdesc[AC]; + goto spout3; + }; + if (p) + { + lprintf (p, lastmonst); + bottomline (); + } + + return (0); +} + + + +/* +* checkloss(x) Routine to subtract hp from user and flag bottomline display +* int x; +* +* Routine to subtract hitpoints from the user and flag the bottomline display +* Enter with the number of hit points to lose +* Note: if x > cdesc[HP] this routine could kill the player! +*/ +void +checkloss (int x) +{ + + if (x > 0) + { + + losehp (x); + bottomhp (); + } +} diff --git a/src/moreobj.c b/src/moreobj.c new file mode 100644 index 0000000..2bde656 --- /dev/null +++ b/src/moreobj.c @@ -0,0 +1,911 @@ +/* moreobj.c + +Routines in this file: + +oaltar() +othrone() +odeadthrone() +ochest() +ofountain() +fntchange() +fch() +drink_fountain() +wash_fountain() +enter() +remove_gems() +sit_on_throne() +up_stairs() +down_stairs() +open_something() +close_something() +desecrate_altar() +pray_at_altar() +*/ +#include +#include "includes/action.h" +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/create.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/inventory.h" +#include "includes/io.h" +#include "includes/moreobj.h" +#include "includes/object.h" +#include "includes/spells.h" +#include "includes/store.h" + +static void fch (int, long *); +static void specify_obj_nocurs (void); +static void specify_obj_cursor (void); +static void move_cursor (int *, int *, int); + + + + +/* +* subroutine to process an altar object +*/ +void +oaltar (void) +{ + + lprcat ("\nDo you (p) pray (d) desecrate"); + iopts (); + for (;;) + { + switch (ttgetch ()) + { + case 'p': + lprcat (" pray\nDo you (m) give money or (j) just pray? "); + for (;;) + switch (ttgetch ()) + { + case 'j': + lprcat ("\n"); + act_just_pray (); + return; + + case 'm': + act_donation_pray (); + return; + + case '\33': + return; + }; + + case 'd': + lprcat (" desecrate"); + act_desecrate_altar (); + return; + + case 'i': + case '\33': + ignore (); + act_ignore_altar (); + return; + }; + } +} + + + +/* +subroutine to process a throne object +*/ +void +othrone (int arg) +{ + + lprcat ("\nDo you (p) pry off jewels, (s) sit down"); + iopts (); + for (;;) + { + for (;;) + { + switch (ttgetch ()) + { + case 'p': + lprcat (" pry off"); + act_remove_gems (arg); + return; + + case 's': + lprcat (" sit down"); + act_sit_throne (arg); + return; + + case 'i': + case '\33': + ignore (); + return; + }; + } + } +} + + + +void +odeadthrone (void) +{ + lprcat ("\nDo you (s) sit down"); + iopts (); + for (;;) + { + for (;;) + { + switch (ttgetch ()) + { + case 's': + lprcat (" sit down"); + act_sit_throne (1); + return; + + case 'i': + case '\33': + ignore (); + return; + }; + } + } +} + + + +/* +* subroutine to process a chest object +*/ +void +ochest (void) +{ + + lprcat ("\nDo you (t) take it, (o) try to open it"); + iopts (); + for (;;) + { + switch (ttgetch ()) + { + case 'o': + lprcat (" open it"); + act_open_chest (playerx, playery); + return; + + case 't': + lprcat (" take"); + if (take (OCHEST, iarg[playerx][playery]) == 0) + item[playerx][playery] = know[playerx][playery] = 0; + return; + + case 'i': + case '\33': + ignore (); + return; + }; + } +} + + + +/* +* process a fountain object +*/ +void +ofountain (void) +{ + cursors (); + lprcat ("\nDo you (d) drink, (w) wash yourself"); + iopts (); + for (;;) + { + switch (ttgetch ()) + { + case 'd': + act_drink_fountain (); + return; + + case '\33': + case 'i': + ignore (); + return; + + case 'w': + act_wash_fountain (); + return; + } + } +} + + + +/* +* a subroutine to raise or lower character levels +* if x > 0 they are raised if x < 0 they are lowered +*/ +void +fntchange (int how) +{ + int j; + + lprc ('\n'); + + switch (rnd (9)) + { + case 1: + lprcat ("Your strength"); + fch (how, &cdesc[STRENGTH]); + break; + case 2: + lprcat ("Your intelligence"); + fch (how, &cdesc[INTELLIGENCE]); + break; + case 3: + lprcat ("Your wisdom"); + fch (how, &cdesc[WISDOM]); + break; + case 4: + lprcat ("Your constitution"); + fch (how, &cdesc[CONSTITUTION]); + break; + case 5: + lprcat ("Your dexterity"); + fch (how, &cdesc[DEXTERITY]); + break; + case 6: + lprcat ("Your charm"); + fch (how, &cdesc[CHARISMA]); + break; + case 7: + j = rnd (level + 1); + if (how < 0) + { + lprintf ("You lose %d hit point", (int) j); + if (j > 1) + lprcat ("s!"); + else + lprc ('!'); + losemhp ((int) j); + } + else + { + lprintf ("You gain %d hit point", (int) j); + if (j > 1) + lprcat ("s!"); + else + lprc ('!'); + raisemhp ((int) j); + } + bottomline (); + break; + + case 8: + j = rnd (level + 1); + if (how > 0) + { + lprintf ("You just gained %d spell", (int) j); + raisemspells ((int) j); + if (j > 1) + lprcat ("s!"); + else + lprc ('!'); + } + else + { + lprintf ("You just lost %d spell", (int) j); + losemspells ((int) j); + if (j > 1) + lprcat ("s!"); + else + lprc ('!'); + } + bottomline (); + break; + + case 9: + j = 5 * rnd ((level + 1) * (level + 1)); + if (how < 0) + { + lprintf ("You just lost %d experience point", (int) j); + if (j > 1) + lprcat ("s!"); + else + lprc ('!'); + loseexperience (j); + } + else + { + lprintf ("You just gained %d experience point", (int) j); + if (j > 1) + lprcat ("s!"); + else + lprc ('!'); + raiseexperience (j); + } + break; + } + cursors (); +} + + + +/* +subroutine to process an up/down of a character attribute for ofountain +*/ +static void +fch (int how, long *x) +{ + if (how < 0) + { + lprcat (" went down by one!"); + --(*x); + } + else + { + lprcat (" went up by one!"); + (*x)++; + } + bottomline (); +} + + + +/* +For command mode. Perform drinking at a fountain. +*/ +void +drink_fountain (void) +{ + cursors (); + if (item[playerx][playery] == ODEADFOUNTAIN) + lprcat ("\nThere is no water to drink!"); + + else if (item[playerx][playery] != OFOUNTAIN) + lprcat ("\nI see no fountain to drink from here!"); + + else + act_drink_fountain (); + return; +} + + + + +/* +For command mode. Perform washing (tidying up) at a fountain. +*/ +void +wash_fountain (void) +{ + cursors (); + if (item[playerx][playery] == ODEADFOUNTAIN) + lprcat ("\nThere is no water to wash in!"); + + else if (item[playerx][playery] != OFOUNTAIN) + lprcat ("\nI see no fountain to wash at here!"); + + else + act_wash_fountain (); + return; +} + + + +/* +For command mode. Perform entering a building. +*/ +void +enter (void) +{ + cursors (); + switch (item[playerx][playery]) + { + case OSCHOOL: + oschool (); + break; + + case OBANK: + obank (); + break; + + case OBANK2: + obank2 (); + break; + + case ODNDSTORE: + dndstore (); + break; + + case OENTRANCE: + /* place player in front of entrance on level 1. newcavelevel() + prevents player from landing on a monster/object. + */ + playerx = 33; + playery = MAXY - 2; + newcavelevel (1); + know[33][MAXY - 1] = KNOWALL; + mitem[33][MAXY - 1] = 0; + draws (0, MAXX, 0, MAXY); + showcell (playerx, playery); /* to show around player */ + bot_linex (); + break; + + case OTRADEPOST: + otradepost (); + break; + + case OLRS: + olrs (); + break; + + case OHOME: + ohome (); + break; + + default: + lprcat ("\nThere is no place to enter here!\n"); + break; + } +} + + + +/* +For command mode. Perform removal of gems from a jeweled throne. +*/ +void +remove_gems (void) +{ + cursors (); + if (item[playerx][playery] == ODEADTHRONE) + lprcat ("\nThere are no gems to remove!"); + + else if (item[playerx][playery] == OTHRONE) + act_remove_gems (0); + + else if (item[playerx][playery] == OTHRONE2) + act_remove_gems (1); + + else + lprcat ("\nI see no throne here to remove gems from!"); + return; +} + + + +/* +For command mode. Perform sitting on a throne. +*/ +void +sit_on_throne (void) +{ + + cursors (); + if (item[playerx][playery] == OTHRONE) + act_sit_throne (0); + + else if ((item[playerx][playery] == OTHRONE2) || + (item[playerx][playery] == ODEADTHRONE)) + act_sit_throne (1); + + else + lprcat ("\nI see no throne to sit on here!"); + + return; +} + + + +/* +For command mode. Checks that player is actually standing at a set up +up stairs or volcanic shaft. +*/ +void +up_stairs (void) +{ + cursors (); + if (item[playerx][playery] == OSTAIRSDOWN) + lprcat ("\nThe stairs don't go up!"); + + else if (item[playerx][playery] == OVOLUP) + act_up_shaft (); + + else if (item[playerx][playery] != OSTAIRSUP) + lprcat ("\nI see no way to go up here!"); + + else + act_up_stairs (); +} + + + + +/* +For command mode. Checks that player is actually standing at a set of +down stairs or volcanic shaft. +*/ +void +down_stairs (void) +{ + cursors (); + if (item[playerx][playery] == OSTAIRSUP) + lprcat ("\nThe stairs don't go down!"); + + else if (item[playerx][playery] == OVOLDOWN) + act_down_shaft (); + + else if (item[playerx][playery] != OSTAIRSDOWN) + lprcat ("\nI see no way to go down here!"); + + else + act_down_stairs (); +} + + + +/* +For command mode. Perform opening an object (door, chest). +*/ +void +open_something (void) +{ + int x, y; /* direction to open */ + char tempc; /* result of prompting to open a chest */ + + cursors (); + /* check for confusion. + */ + if (cdesc[CONFUSE]) + { + lprcat ("You're too confused!"); + return; + } + + /* check for player standing on a chest. If he is, prompt for and + let him open it. If player ESCs from prompt, quit the Open + command. + */ + if (item[playerx][playery] == OCHEST) + { + lprcat ("There is a chest here. Open it?"); + if ((tempc = getyn ()) == 'y') + { + act_open_chest (playerx, playery); + dropflag = 1; /* prevent player from picking back up if fail */ + return; + } + else if (tempc != 'n') + return; + } + + /* get direction of object to open. test 'openability' of object + indicated, call common command/prompt mode routines to actually open. + */ + dirsub (&x, &y); + switch (item[x][y]) + { + case OOPENDOOR: + lprcat ("The door is already open!"); + break; + + case OCHEST: + act_open_chest (x, y); + break; + + case OCLOSEDDOOR: + act_open_door (x, y); + break; + +/* This message is rephrased to handle other scenarios. -Gibbon */ + default: + lprcat ("\nNothing happens.."); + break; + } +} + + + + +/* +For command mode. Perform the action of closing something (door). +*/ +void +close_something (void) +{ + int x, y; + + cursors (); + /* check for confusion. + */ + if (cdesc[CONFUSE]) + { + lprcat ("You're too confused!"); + return; + } + + /* get direction of object to close. test 'closeability' of object + indicated. + */ + dirsub (&x, &y); + switch (item[x][y]) + { + case OCLOSEDDOOR: + lprcat ("The door is already closed!"); + break; + + case OOPENDOOR: + if (mitem[x][y]) + { + lprcat ("Theres a monster in the way!"); + return; + } + item[x][y] = OCLOSEDDOOR; + know[x][y] = 0; + iarg[x][y] = 0; + break; + + default: + lprcat ("You can't close that!"); + break; + } +} + + + + +/* +* For command mode. Perform the act of descecrating an altar. +*/ +void +desecrate_altar (void) +{ + cursors (); + if (item[playerx][playery] == OALTAR) + act_desecrate_altar (); + else + lprcat ("\nI see no altar to desecrate here!"); +} + + + + + +/* +For command mode. Perform the act of praying at an altar. +*/ +void +pray_at_altar (void) +{ + + cursors (); + if (item[playerx][playery] != OALTAR) + lprcat ("\nI see no altar to pray at here!"); + else + act_donation_pray (); + prayed = 1; +} + + + +/* +Identify objects for the player. +*/ +void +specify_object (void) +{ + cursors (); + lprcat ("\nIdentify unknown object by cursor [ynq]?"); + + for (;;) + { + + switch (ttgetch ()) + { + case '\33': + case 'q': + return; + case 'y': + case 'Y': + specify_obj_cursor (); + return; + case 'n': + case 'N': + specify_obj_nocurs (); + return; + } + } +} + + + +/* perform the actions of identifying the object/monster associated with a +character typed by the user. assumes cursors(). +*/ +static void +specify_obj_nocurs (void) +{ + char i; + int j, flag; + + lprcat ("\nType object character:"); + switch (i = ttgetch ()) + { + case '\33': + case '\n': + return; + case '@': + lprintf ("\n@: %s", logname); + return; + case ' ': + lprintf ("\n : An as-yet-unseen place in the dungeon"); + return; + default: + if (i == floorc) + { + lprc ('\n'); + lprc (floorc); + lprintf (": the floor of the dungeon"); + return; + } + flag = FALSE; + for (j = 0; j < MAXMONST + 8; j++) + if (i == monstnamelist[j]) + { + lprintf ("\n%c: %s", i, monster[j].name); + flag = TRUE; + } + /* check for spurious object character + */ + if (i != '_') + for (j = 0; j < MAXOBJECT; j++) + if (i == objnamelist[j]) + { + lprc ('\n'); + lprc (i); + lprintf (": %s", objectname[j]); + flag = TRUE; + } + if (!flag) + lprintf ("\n%c: unknown monster/object", i); + return; + } +} + + +static void +specify_obj_cursor (void) +{ + int objx, objy; + int i; + + lprcat ("\nMove the cursor to an unknown item."); + lprcat ("\n(For instructions type a ?)"); + + objx = playerx; + objy = playery; + cursor (objx + 1, objy + 1); + /* make cursor visible. + */ + for (;;) + { + switch (ttgetch ()) + { + case '?': + cursors (); + lprcat + ("\nUse [hjklnbyu] to move the cursor to the unknown object."); + lprcat ("\nType a . when the cursor is at the desired place."); + lprcat ("\nType q, Return, or Escape to exit."); + cursor (objx + 1, objy + 1); + break; + + case '\33': + case 'q': + case '\n': + /* reset cursor + */ + cursor (playerx + 1, playery + 1); + return; + case '.': + /* reset cursor + */ + cursor (playerx + 1, playery + 1); + cursors (); + + if ((objx == playerx) && (objy == playery)) + { + lprintf ("\n@: %s", logname); + return; + } + + i = mitem[objx][objy]; + if (i && (know[objx][objy] & KNOWHERE)) + + /* check for invisible monsters and not display + */ + if (monstnamelist[i] != floorc) + { + lprintf ("\n%c: %s", monstnamelist[i], monster[i].name); + return; + } + + /* handle floor separately so as not to display traps, etc. + */ + i = item[objx][objy]; + if (i == 0) + { + lprc ('\n'); + lprc (floorc); + lprintf (": the floor of the dungeon"); + return; + } + + if (know[objx][objy] & HAVESEEN) + { + lprc ('\n'); + lprc (objnamelist[i]); + lprintf (": %s", objectname[i]); + return; + } + + lprintf ("\n : An as-yet-unseen place in the dungeon"); + return; + + case 'H': + case 'h': + move_cursor (&objx, &objy, 4); + break; + case 'J': + case 'j': + move_cursor (&objx, &objy, 1); + break; + case 'K': + case 'k': + move_cursor (&objx, &objy, 3); + break; + case 'L': + case 'l': + move_cursor (&objx, &objy, 2); + break; + case 'B': + case 'b': + move_cursor (&objx, &objy, 8); + break; + case 'N': + case 'n': + move_cursor (&objx, &objy, 7); + break; + case 'Y': + case 'y': + move_cursor (&objx, &objy, 6); + break; + case 'U': + case 'u': + move_cursor (&objx, &objy, 5); + break; + default: + break; + } + } +} + + + +static void +move_cursor (int *xx, int *yy, int cdir) +{ + + *xx += diroffx[cdir]; + *yy += diroffy[cdir]; + + if (*yy < 0) + *yy = MAXY - 1; + if (*yy > MAXY - 1) + *yy = 0; + if (*xx < 0) + *xx = MAXX - 1; + if (*xx > MAXX - 1) + *xx = 0; + + cursor (*xx + 1, *yy + 1); +} diff --git a/src/movem.c b/src/movem.c new file mode 100644 index 0000000..c84ddbf --- /dev/null +++ b/src/movem.c @@ -0,0 +1,722 @@ +/* +* movem.c (move monster) +* +* movemonst() Routine to move the monsters toward the player +* build_proximity_ripple() Build proximity ripple for smart monster move +* move_scared() Move scared monsters +* move_smart() Move smart monsters +* move_dumb() Move dumb monsters +* mmove(x,y,xd,yd) Function to actually perform the monster movement +*/ +#include +#include "includes/create.h" +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/io.h" +#include "includes/monster.h" +#include "includes/movem.h" +#include "includes/spheres.h" + +static void build_proximity_ripple (void); +static void move_scared (int, int); +static void move_smart (int, int); +static void move_dumb (int, int); +static void mmove (int, int, int, int); + + +#if 0 +#define IDISTNORM 8 /* was 17 - dgk */ +#define IDISTAGGR 20 /* was 40 - dgk */ +#endif +#define IDISTNORM 17 /* was 17 - dgk */ +#define IDISTAGGR 40 /* was 40 - dgk */ + +static int w1x[9], w1y[9]; +static int tmp1, tmp2, tmp3, tmp4, distance; + +/* list of monsters to move */ +static struct foo +{ + int x; + int y; + int smart; +} movelist[250]; + + + +/* +* movemonst() Routine to move the monsters toward the player +* +* This routine has the responsibility to determine which monsters are to +* move, and call movemt() to do the move. +* Returns no value. +*/ +void +movemonst (void) +{ + int i, j, movecnt = 0, smart_count, min_int; + + if (cdesc[HOLDMONST]) + return; /* no action if monsters are held */ + + if (cdesc[AGGRAVATE]) /* determine window of monsters to move */ + { + tmp1 = playery - 5; + tmp2 = playery + 6; + tmp3 = playerx - 10; + tmp4 = playerx + 11; + distance = IDISTAGGR; /* depth of intelligent monster movement */ + } + else + { + tmp1 = playery - 3; + tmp2 = playery + 4; + tmp3 = playerx - 5; + tmp4 = playerx + 6; + distance = IDISTNORM; /* depth of intelligent monster movement */ + } + + if (level == 0) /* if on outside level monsters can move in perimeter */ + { + if (tmp1 < 0) + tmp1 = 0; + if (tmp2 > MAXY) + tmp2 = MAXY; + if (tmp3 < 0) + tmp3 = 0; + if (tmp4 > MAXX) + tmp4 = MAXX; + } + else /* if in a dungeon monsters can't be on the perimeter (wall there) */ + { + if (tmp1 < 1) + tmp1 = 1; + if (tmp2 > MAXY - 1) + tmp2 = MAXY - 1; + if (tmp3 < 1) + tmp3 = 1; + if (tmp4 > MAXX - 1) + tmp4 = MAXX - 1; + } + + /* We now have a window in which to move monsters. First find all + monsters in the window, then decide whether or not to move them. + Its faster that way since the size of the window is usually larger + than the # of monsters in that window. + + Find all monsters in the window. The only time a monster cannot + move is if: monsters are not aggrevated, AND player is stealthed, + AND the monster is asleep due to stealth. Split into two + separate loops in order to simplify the if statement inside the + loop for the most common case. + + Also count # of smart monsters. + */ + smart_count = 0; + min_int = 10 - cdesc[HARDGAME]; /* minimum monster intelligence to move smart */ + if (cdesc[AGGRAVATE] || !cdesc[STEALTH]) + { + for (j = tmp1; j < tmp2; j++) + for (i = tmp3; i < tmp4; i++) + if (mitem[i][j]) + { + movelist[movecnt].x = i; + movelist[movecnt].y = j; + if (monster[mitem[i][j]].intelligence > min_int) + { + movelist[movecnt].smart = TRUE; + smart_count++; + } + else + movelist[movecnt].smart = FALSE; + movecnt++; + } + } + else + { + for (j = tmp1; j < tmp2; j++) + for (i = tmp3; i < tmp4; i++) + if (mitem[i][j] && stealth[i][j]) /* stealth[x][y] = 1 when AWAKE! */ + { + movelist[movecnt].x = i; + movelist[movecnt].y = j; + if (monster[mitem[i][j]].intelligence > min_int) + { + movelist[movecnt].smart = TRUE; + smart_count++; + } + else + movelist[movecnt].smart = FALSE; + movecnt++; + } + } + + /* now move the monsters in the movelist. If we have at least one + smart monster, build a proximity ripple and use it for all smart + monster movement. + */ + if (movecnt > 0) + { + if (cdesc[SCAREMONST]) + for (i = 0; i < movecnt; i++) + move_scared (movelist[i].x, movelist[i].y); + else + { + if (smart_count > 0) + { + /* I was going to put in code that prevented the rebuilding + of the proximity ripple if the player had not moved since + the last turn. Unfortunately, this permits the player to + blast down doors to treasure rooms and not have a single + intelligent monster move. + */ + build_proximity_ripple (); + for (i = 0; i < movecnt; i++) + if (movelist[i].smart) + move_smart (movelist[i].x, movelist[i].y); + else + move_dumb (movelist[i].x, movelist[i].y); + } + else + for (i = 0; i < movecnt; i++) + move_dumb (movelist[i].x, movelist[i].y); + } + } + + /* Also check for the last monster hit. This is necessary to prevent + the player from getting free hits on a monster with long range + spells or when stealthed. + */ + if (cdesc[AGGRAVATE] || !cdesc[STEALTH]) + { + /* If the last monster hit is within the move window, its already + been moved. + */ + if (((lasthx < tmp3 || lasthx >= tmp4) || + (lasthy < tmp1 || lasthy >= tmp2)) && mitem[lasthx][lasthy]) + { + if (cdesc[SCAREMONST]) + move_scared (lasthx, lasthy); + else if (monster[mitem[lasthx][lasthy]].intelligence > min_int) + { + if (smart_count == 0) + build_proximity_ripple (); + move_smart (lasthx, lasthy); + } + else + move_dumb (lasthx, lasthy); + lasthx = w1x[0]; /* make sure the monster gets moved again */ + lasthy = w1y[0]; + } + } + else + { + /* If the last monster hit is within the move window, and not + asleep due to stealth, then it has already been moved. + Otherwise (monster outside window, asleep due to stealth), + move the monster and update the lasthit x,y position. + */ + if ((((lasthx < tmp3 || lasthx >= tmp4) || + (lasthy < tmp1 || lasthy >= tmp2)) && + mitem[lasthx][lasthy]) || !stealth[lasthx][lasthy]) + { + if (cdesc[SCAREMONST]) + move_scared (lasthx, lasthy); + else if (monster[mitem[lasthx][lasthy]].intelligence > min_int) + { + if (smart_count == 0) + build_proximity_ripple (); + move_smart (lasthx, lasthy); + } + else + move_dumb (lasthx, lasthy); + lasthx = w1x[0]; /* make sure the monster gets moved again */ + lasthy = w1y[0]; + } + } +} + + + + +int screen[MAXX][MAXY]; /* proximity ripple storage */ + +/* queue for breadth-first 'search' build of proximity ripple. +*/ +#define MAX_QUEUE 100 +static struct queue_entry +{ + int x; + int y; + int distance; +} queue[MAX_QUEUE]; +static int queue_head = 0; +static int queue_tail = 0; + +/* put a location on the proximity ripple queue +*/ +#define PUTQUEUE( _x, _y, _d ) \ +{ \ + queue[queue_tail].x = (_x) ; \ + queue[queue_tail].y = (_y) ; \ + queue[queue_tail].distance = (_d); \ + queue_tail++; \ + if (queue_tail == MAX_QUEUE) \ + queue_tail = 0 ; \ +} + +/* take a location from the proximity ripple queue +*/ +#define GETQUEUE( _x, _y, _d ) \ +{ \ + (_x) = queue[queue_head].x ; \ + (_y) = queue[queue_head].y ; \ + (_d) = queue[queue_head].distance ; \ + queue_head++; \ + if (queue_head == MAX_QUEUE) \ + queue_head = 0 ; \ +} + +/* check for the proximity ripple queue being empty +*/ +#define QUEUEEMPTY() (queue_head == queue_tail) + +/* +For smart monster movement, build a proximity ripple from the player's +position, out to a 'distance' of 20. For example: + +W 5 4 4 W W X Player is at position marked 1 +W 5 W 3 3 W W W is a wall. Monsters will attempt +W 6 W 2 W 4 W to move to a location with a smaller +W 7 W 1 W 5 W value than their current position. +W 8 W W W 6 W Note that a monster at location X +W 9 9 8 7 7 7 will not move at all. +W W W 8 W W W +*/ +static void +build_proximity_ripple (void) +{ + int xl, yl, xh, yh; + int k, m, z, tmpx, tmpy; + int curx, cury, curdist; + + xl = tmp3 - 2; + yl = tmp1 - 2; + xh = tmp4 + 2; + yh = tmp2 + 2; + vxy (&xl, &yl); + vxy (&xh, &yh); + for (k = yl; k <= yh; k++) + for (m = xl; m <= xh; m++) + { + switch (item[m][k]) + { + case OWALL: + case OPIT: + case OTRAPARROW: + case ODARTRAP: + case OCLOSEDDOOR: + case OTRAPDOOR: + case OTELEPORTER: + screen[m][k] = 127; + break; + case OENTRANCE: + if (level == 1) + screen[m][k] = 127; + else + screen[m][k] = 0; + break; + default: + screen[m][k] = 0; + break; + }; + } + screen[playerx][playery] = 1; + + /* now perform proximity ripple from playerx,playery to monster */ + xl = tmp3 - 1; + yl = tmp1 - 1; + xh = tmp4 + 1; + yh = tmp2 + 1; + vxy (&xl, &yl); + vxy (&xh, &yh); + + PUTQUEUE (playerx, playery, 1); + do + { + GETQUEUE (curx, cury, curdist); + + /* test all spots around the current one being looked at. + */ + if ((curx >= xl && curx <= xh) && (cury >= yl && cury <= yh)) + { + for (z = 1; z < 9; z++) + { + tmpx = curx + diroffx[z]; + tmpy = cury + diroffy[z]; + vxy (&tmpx, &tmpy); + if (screen[tmpx][tmpy] == 0) + { + screen[tmpx][tmpy] = curdist + 1; + PUTQUEUE (tmpx, tmpy, curdist + 1); + } + } + } + } + while (!QUEUEEMPTY ()); +} + + + +/* +Move scared monsters randomly away from the player position. +*/ +static void +move_scared (int i, int j) +{ + int xl, yl, tmp; + + /* check for a half-speed monster, and check if not to move. Could be + done in the monster list build. + */ + switch (mitem[i][j]) + { + case TROGLODYTE: + case HOBGOBLIN: + case METAMORPH: + case XVART: + case INVISIBLESTALKER: + case ICELIZARD: + if ((gtime & 1) == 1) + return; + }; + + if ((xl = i + rnd (3) - 2) < 0) + xl = 0; + if (xl >= MAXX) + xl = MAXX - 1; + if ((yl = j + rnd (3) - 2) < 0) + yl = 0; + if (yl >= MAXY) + yl = MAXY - 1; + + if ((tmp = item[xl][yl]) != OWALL) + if (mitem[xl][yl] == 0) + if ((mitem[i][j] != VAMPIRE) || (tmp != OMIRROR)) + if (tmp != OCLOSEDDOOR) + mmove (i, j, xl, yl); +} + + + +/* +Move monsters that are moving intelligently, using the proximity +ripple. Attempt to move to a position in the proximity ripple +that is closer to the player. + +Parameters: the X,Y position of the monster to be moved. +*/ +static void +move_smart (int i, int j) +{ + int x, y, z; + + /* check for a half-speed monster, and check if not to move. Could be + done in the monster list build. + */ + switch (mitem[i][j]) + { + case TROGLODYTE: + case HOBGOBLIN: + case METAMORPH: + case XVART: + case INVISIBLESTALKER: + case ICELIZARD: + if ((gtime & 1) == 1) + return; + }; + + /* find an adjoining location in the proximity ripple that is + closer to the player (has a lower value) than the monster's + current position. + */ + if (mitem[i][j] != VAMPIRE) + for (z = 1; z < 9; z++) /* go around in a circle */ + { + x = i + diroffx[z]; + y = j + diroffy[z]; + if (screen[x][y] < screen[i][j]) + if (!mitem[x][y]) + { + mmove (i, j, w1x[0] = x, w1y[0] = y); + return; + } + } + else + /* prevent vampires from moving onto mirrors + */ + for (z = 1; z < 9; z++) /* go around in a circle */ + { + x = i + diroffx[z]; + y = j + diroffy[z]; + if ((screen[x][y] < screen[i][j]) && (item[x][y] != OMIRROR)) + if (!mitem[x][y]) + { + mmove (i, j, w1x[0] = x, w1y[0] = y); + return; + } + } + +} + + + + +/* +For monsters that are not moving in an intelligent fashion. Move +in a direct fashion toward the player's current position. + +Parameters: the X,Y position of the monster to move. +*/ +static void +move_dumb (int i, int j) +{ + int xl, yl, xh, yh; + int k, m, tmp, tmpd, tmpx, tmpy; + + /* check for a half-speed monster, and check if not to move. Could be + done in the monster list build. + */ + switch (mitem[i][j]) + { + case TROGLODYTE: + case HOBGOBLIN: + case METAMORPH: + case XVART: + case INVISIBLESTALKER: + case ICELIZARD: + if ((gtime & 1) == 1) + return; + }; + + /* dumb monsters move here */ + /* set up range of spots to check. instead of checking all points + around the monster, only check those closest to the player. For + example, if the player is up and right of the monster, check only + the three spots up and right of the monster. + */ + xl = i - 1; + yl = j - 1; + xh = i + 2; + yh = j + 2; + if (i < playerx) + xl++; + else if (i > playerx) + --xh; + if (j < playery) + yl++; + else if (j > playery) + --yh; + + if (xl < 0) + xl = 0; + if (yl < 0) + yl = 0; + if (xh > MAXX) + xh = MAXX; /* MAXX OK; loop check below is <, not <= */ + if (yh > MAXY) + yh = MAXY; /* MAXY OK; loop check below is <, not <= */ + + /* check all spots in the range. find the one that is closest to + the player. if the monster is already next to the player, exit + the check immediately. + */ + tmpd = 10000; + tmpx = i; + tmpy = j; + for (k = xl; k < xh; k++) + for (m = yl; m < yh; m++) + if (k == playerx && m == playery) + { + tmpd = 1; + tmpx = k; + tmpy = m; + break; /* exitloop */ + } + else if ((item[k][m] != OWALL) && + (item[k][m] != OCLOSEDDOOR) && + ((mitem[k][m] == 0) || ((k == i) && (m == j))) && + ((mitem[i][j] != VAMPIRE) || (item[k][m] != OMIRROR))) + { + tmp = (playerx - k) * (playerx - k) + (playery - m) * (playery - m); + if (tmp < tmpd) + { + tmpd = tmp; + tmpx = k; + tmpy = m; + } /* end if */ + } /* end if */ + + /* we have finished checking the spaces around the monster. if + any can be moved on and are closer to the player than the + current location, move the monster. + */ + if ((tmpd < 10000) && ((tmpx != i) || (tmpy != j))) + { + mmove (i, j, tmpx, tmpy); + w1x[0] = tmpx; /* for last monster hit */ + w1y[0] = tmpy; + } + else + { + w1x[0] = i; /* for last monster hit */ + w1y[0] = j; + } +} /* end move_dumb() */ + + + + +/* +* mmove(x,y,xd,yd) Function to actually perform the monster movement +* int x,y,xd,yd; +* +* Enter with the from coordinates in (x,y) and the destination coordinates +* in (xd,yd). +*/ +static void +mmove (int aa, int bb, int cc, int dd) +{ + int tmp, i, flag; + char *who = ""; + char *p = ""; + + flag = 0; /* set to 1 if monster hit by arrow trap */ + if ((cc == playerx) && (dd == playery)) + { + hitplayer (aa, bb); + return; + } + i = item[cc][dd]; + if ((i == OPIT) || (i == OTRAPDOOR)) + switch (mitem[aa][bb]) + { + case BAT: + case EYE: + case SPIRITNAGA: + case PLATINUMDRAGON: + case WRAITH: + case VAMPIRE: + case SILVERDRAGON: + case POLTERGEIST: + case DEMONLORD: + case DEMONLORD + 1: + case DEMONLORD + 2: + case DEMONLORD + 3: + case DEMONLORD + 4: + case DEMONLORD + 5: + case DEMONLORD + 6: + case DEMONPRINCE: + break; + + default: + mitem[aa][bb] = 0; /* fell in a pit or trapdoor */ + }; + tmp = mitem[aa][bb]; + mitem[cc][dd] = tmp; + if (i == OANNIHILATION) + { + if (tmp >= DEMONLORD + 3) /* demons dispel spheres */ + { + cursors (); + lprintf ("\nThe %s dispels the sphere!", monster[tmp].name); + rmsphere (cc, dd); /* delete the sphere */ + } + else + mitem[cc][dd] = i = tmp = 0; + } + stealth[cc][dd] = 1; + if ((hitp[cc][dd] = hitp[aa][bb]) < 0) + hitp[cc][dd] = 1; + mitem[aa][bb] = 0; + if (tmp == LEPRECHAUN) + switch (i) + { + case OGOLDPILE: + case OMAXGOLD: + case OKGOLD: + case ODGOLD: + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + item[cc][dd] = 0; /* leprechaun takes gold */ + }; + + if (tmp == TROLL) /* if a troll regenerate him */ + if ((gtime & 1) == 0) + if (monster[tmp].hitpoints > hitp[cc][dd]) + hitp[cc][dd]++; + + if (i == OTRAPARROW) /* arrow hits monster */ + { + who = "An arrow"; + if ((hitp[cc][dd] -= rnd (10) + level) <= 0) + { + mitem[cc][dd] = 0; + flag = 2; + } + else + flag = 1; + } + if (i == ODARTRAP) /* dart hits monster */ + { + who = "A dart"; + if ((hitp[cc][dd] -= rnd (6)) <= 0) + { + mitem[cc][dd] = 0; + flag = 2; + } + else + flag = 1; + } + if (i == OTELEPORTER) /* monster hits teleport trap */ + { + flag = 3; + fillmonst (mitem[cc][dd]); + mitem[cc][dd] = 0; + } + if (cdesc[BLINDCOUNT]) + return; /* if blind don't show where monsters are */ + if (know[cc][dd] & HAVESEEN) + { + p = 0; + if (flag) + cursors (); + switch (flag) + { + case 1: + p = "\n%s hits the %s"; + break; + case 2: + p = "\n%s hits and kills the %s"; + break; + case 3: + p = "\nThe %s%s gets teleported"; + who = ""; + break; + }; + if (p) + { + lprintf (p, who, monster[tmp].name); + } + } + /* if (yrepcount>1) { know[aa][bb] &= 2; know[cc][dd] &= 2; return; } */ + if (know[aa][bb] & HAVESEEN) + show1cell (aa, bb); + if (know[cc][dd] & HAVESEEN) + show1cell (cc, dd); +} diff --git a/src/object.c b/src/object.c new file mode 100644 index 0000000..28b358f --- /dev/null +++ b/src/object.c @@ -0,0 +1,1506 @@ +/* object.c */ +#include +#include +#include "includes/action.h" +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/create.h" +#include "includes/display.h" +#include "includes/fortune.h" +#include "includes/global.h" +#include "includes/inventory.h" +#include "includes/io.h" +#include "includes/main.h" +#include "includes/monster.h" +#include "includes/moreobj.h" +#include "includes/object.h" +#include "includes/regen.h" +#include "includes/scores.h" +#include "includes/spells.h" +#include "includes/sysdep.h" + +static void ostairs (int); + +static void opotion (int); + +static void oscroll (int); + +static void opit (void); +static void obottomless (void); +static void ostatue (void); +static void omirror (void); +static void obook (void); + +static void ocookie (void); + +static void ogold (int); + +static void prompt_enter (void); + +static void prompt_volshaft (int); + +static void o_open_door (void); +static void o_closed_door (void); + + + + +/* LOOK_FOR_OBJECT +subroutine to look for an object and give the player his options if an object +was found. + +do_ident; identify item: T/F +do_pickup; pickup item: T/F +do_action; prompt for actions on object: T/F +*/ +void +lookforobject (char do_ident, char do_pickup, char do_action) +{ + int i, j; + + /* can't find objects if time is stopped */ + if (cdesc[TIMESTOP]) + return; + i = item[playerx][playery]; + if (i == 0) + return; + j = iarg[playerx][playery]; + showcell (playerx, playery); + cursors (); + yrepcount = 0; + switch (i) + { + case OGOLDPILE: + case OMAXGOLD: + case OKGOLD: + case ODGOLD: + lprcat ("\nYou have found some gold!"); + ogold (i); + break; + + case OPOTION: + if (do_ident) + { + lprcat ("\nYou have found a magic potion"); + if (potionname[j][0]) + lprintf (" of %s", &potionname[j][1]); + } + if (do_pickup) + if (take (OPOTION, j) == 0) + forget (); + if (do_action) + opotion (j); + break; + + case OSCROLL: + if (do_ident) + { + lprcat ("\nYou have found a magic scroll"); + if (scrollname[j][0]) + lprintf (" of %s", &scrollname[j][1]); + } + if (do_pickup) + if (take (OSCROLL, j) == 0) + forget (); + if (do_action) + oscroll (j); + break; + + case OALTAR: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nThere is a Holy Altar here!"); + if (do_action) + oaltar (); + break; + + case OBOOK: + if (do_ident) + lprcat ("\nYou have found a book."); + if (do_pickup) + if (take (OBOOK, j) == 0) + forget (); + if (do_action) + obook (); + break; + + case OCOOKIE: + if (do_ident) + lprcat ("\nYou have found a fortune cookie."); + if (do_pickup) + if (take (OCOOKIE, 0) == 0) + forget (); + if (do_action) + ocookie (); + break; + + case OTHRONE: + if (nearbymonst ()) + return; + if (do_ident) + lprintf ("\nThere is %s here!", objectname[i]); + if (do_action) + othrone (0); + break; + + case OTHRONE2: + if (nearbymonst ()) + return; + if (do_ident) + lprintf ("\nThere is %s here!", objectname[i]); + if (do_action) + othrone (1); + break; + + case ODEADTHRONE: + if (do_ident) + lprintf ("\nThere is %s here!", objectname[i]); + if (do_action) + odeadthrone (); + break; + + case OPIT: + /* always perform these actions. */ + lprcat ("\nYou're standing at the top of a pit."); + opit (); + break; + + case OSTAIRSUP: /* up */ + if (do_ident) + lprcat ("\nThere is a circular staircase here"); + if (do_action) + ostairs(1); + refresh(); + break; + + case OFOUNTAIN: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nThere is a fountain here"); + if (do_action) + ofountain (); + break; + + case OSTATUE: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nYou are standing in front of a statue"); + if (do_action) + ostatue (); + break; + + case OCHEST: + if (do_ident) + lprcat ("\nThere is a chest here"); + if (do_pickup) + if (take (OCHEST, j) == 0) + forget (); + if (do_action) + ochest (); + break; + + case OSCHOOL: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nYou have found the College of Larn."); + if (do_action) + prompt_enter (); + break; + + case OMIRROR: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nThere is a mirror here"); + if (do_action) + omirror (); + break; + + case OBANK2: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nYou have found a branch office of the bank of Larn."); + if (do_action) + prompt_enter (); + break; + + case OBANK: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nYou have found the bank of Larn."); + if (do_action) + prompt_enter (); + break; + + case ODEADFOUNTAIN: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nThere is a dead fountain here"); + break; + + case ODNDSTORE: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nThere is a DND store here."); + if (do_action) + prompt_enter (); + break; + + case OSTAIRSDOWN: /* down */ + if (do_ident) + lprcat ("\nThere is a circular staircase here"); + if (do_action) + ostairs(-1); + refresh(); + break; + + case OOPENDOOR: + if (do_ident) + lprintf ("\nYou have found %s", objectname[i]); + if (do_action) + o_open_door (); + break; + + case OCLOSEDDOOR: + if (do_ident) + lprintf ("\nYou have found %s", objectname[i]); + if (do_action) + o_closed_door (); + break; + + case OENTRANCE: + if (do_ident) + lprcat ("\nYou have found "); + lprcat (objectname[i]); + if (do_action) + prompt_enter (); + break; + + case OVOLDOWN: + if (do_ident) + lprcat ("\nYou have found "); + lprcat (objectname[i]); + if (do_action) + prompt_volshaft (-1); + break; + + case OVOLUP: + if (do_ident) + lprcat ("\nYou have found "); + lprcat (objectname[i]); + if (do_action) + prompt_volshaft (1); + break; + + case OIVTELETRAP: + if (rnd (11) < 6) + return; + item[playerx][playery] = OTELEPORTER; + know[playerx][playery] = KNOWALL; + /* fall through to OTELEPORTER case below!!! */ + + case OTELEPORTER: + lprcat ("\nZaaaappp! You've been teleported!\n"); + nap (NAPTIME); + oteleport (0); + refresh(); + break; + + case OTRAPARROWIV: /* for an arrow trap */ + if (rnd (17) < 13) + return; + item[playerx][playery] = OTRAPARROW; + know[playerx][playery] = 0; + /* fall through to OTRAPARROW case below!!! */ + + case OTRAPARROW: + lprcat ("\nYou are hit by an arrow"); + lastnum = 259; + losehp (rnd (10) + level); + bottomhp (); + return; + + case OIVDARTRAP: /* for a dart trap */ + if (rnd (17) < 13) + return; + item[playerx][playery] = ODARTRAP; + know[playerx][playery] = 0; + /* fall through to ODARTTRAP case below!!! */ + + case ODARTRAP: + lprcat ("\nYou are hit by a dart"); + lastnum = 260; + losehp (rnd (5)); + if ((--cdesc[STRENGTH]) < 3) + cdesc[STRENGTH] = 3; + bottomline (); + return; + + case OIVTRAPDOOR: /* for a trap door */ + if (rnd (17) < 13) + return; + item[playerx][playery] = OTRAPDOOR; + know[playerx][playery] = KNOWALL; + /* fall through to OTRAPDOOR case below!!! */ + + case OTRAPDOOR: + lastnum = 272; /* a trap door */ + if ((level == MAXLEVEL - 1) || (level == MAXLEVEL + MAXVLEVEL - 1)) + { + lprcat ("\nYou fell through a bottomless trap door!"); + nap (NAPTIME); + died (271); + } + i = rnd (5 + level); + lprintf ("\nYou fall through a trap door! You lose %d hit points.", + (long) i); + losehp(i); + nap(NAPTIME); + newcavelevel(level + 1); + draws(0, MAXX, 0, MAXY); + bot_linex(); + return; + + case OTRADEPOST: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nYou have found the Larn trading Post."); + if (do_action) + prompt_enter (); + return; + + case OHOME: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nYou have found your way home."); + if (do_action) + prompt_enter (); + return; + + case OWALL: + break; + + case OANNIHILATION: + died (283); /* annihilated by sphere of annihilation */ + return; + + case OLRS: + if (nearbymonst ()) + return; + if (do_ident) + lprcat ("\nThere is an LRS office here."); + if (do_action) + prompt_enter (); + break; + + default: + if (do_ident) + { + lprintf ("\nYou have found %s ", objectname[i]); + switch (i) + { + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + case OSPIRITSCARAB: + case OORBOFDRAGON: + case OCUBEofUNDEAD: + case ONOTHEFT: + break; + + default: + if (j > 0) + lprintf ("+ %d", (int) j); + else if (j < 0) + lprintf (" %d", (int) j); + break; + } + } + if (do_pickup) + if (take (i, j) == 0) + forget (); + if (do_action) + { + char tempc = 0; + + lprcat ("\nDo you want to (t) take it"); + iopts (); + while (tempc != 't' && tempc != 'i' && tempc != '\33') + tempc = ttgetch (); + if (tempc == 't') + { + lprcat ("take"); + if (take (i, j) == 0) + forget (); + return; + } + ignore (); + } + break; + }; +} + + + +/* +* subroutine to process the stair cases if dir > 0 the up else down +*/ +static void +ostairs (int dir) +{ + + lprcat ("\nDo you (s) stay here "); + if (dir > 0) + lprcat ("or (u) go up? "); + else + lprcat ("or (d) go down? "); + + for (;;) + { + switch (ttgetch ()) + { + case '\33': + case 's': + case 'i': + lprcat ("stay here"); + return; + + case 'u': + lprcat ("go up"); + act_up_stairs (); + return; + + case 'd': + lprcat ("go down"); + act_down_stairs (); + return; + }; + } +} + + + +/* +* subroutine to handle a teleport trap +/- 1 level maximum +*/ +void +oteleport (int err) +{ + int tmp; + if (err) + if (rnd (151) < 3) + { + /* Fix for bug #10 ~Gibbon*/ + cursor(1,19); + lprcat("\nYou died by teleporting into solid rock."); + nap(4000); + died(264); /* stuck in a rock */ + } + cdesc[TELEFLAG] = 1; /* show ?? on bottomline if been teleported */ + if (level == 0) + tmp = 0; + else if (level < MAXLEVEL) + { + tmp = rnd (5) + level - 3; + if (tmp >= MAXLEVEL) + tmp = MAXLEVEL - 1; + if (tmp < 1) + tmp = 1; + } + else + { + tmp = rnd (3) + level - 2; + if (tmp >= MAXLEVEL + MAXVLEVEL) + tmp = MAXLEVEL + MAXVLEVEL - 1; + if (tmp < MAXLEVEL) + tmp = MAXLEVEL; + } + playerx = rnd (MAXX - 2); + playery = rnd (MAXY - 2); + if (level != tmp) + { + newcavelevel (tmp); + positionplayer (); + draws (0, MAXX, 0, MAXY); + bot_linex (); + refresh(); + } +} + +/* +* function to process a potion +*/ +static void +opotion (int pot) +{ + + lprcat ("\nDo you (d) drink it, (t) take it"); + iopts (); + + for (;;) + { + switch (ttgetch ()) + { + case '\33': + case 'i': + ignore (); + return; + + case 'd': + lprcat ("drink\n"); + forget (); /* destroy potion */ + quaffpotion (pot, TRUE); + return; + + case 't': + lprcat ("take\n"); + if (take (OPOTION, pot) == 0) + forget (); + return; + }; + } +} + + + +/* +* function to drink a potion +* +* Also used to perform the action of a potion without quaffing a potion (see +* invisible capability when drinking from a fountain). +*/ +void +quaffpotion (int pot, int set_known) +{ + int i, j, k; + + /* check for within bounds */ + if (pot < 0 || pot >= MAXPOTION) + return; + + /* + * if player is to know this potion (really quaffing one), make it + * known + */ + if (set_known) + potionname[pot][0] = ' '; + + switch (pot) + { + case 0: + lprcat ("\nYou fall asleep. . ."); + i = rnd (11) - (cdesc[CONSTITUTION] >> 2) + 2; + while (--i > 0) + { + parse2 (); + nap (NAPTIME); + } + cursors (); + lprcat ("\nYou woke up!"); + return; + + case 1: + lprcat ("\nYou feel better"); + if (cdesc[HP] == cdesc[HPMAX]) + raisemhp (1); + else if ((cdesc[HP] += rnd (20) + 20 + cdesc[LEVEL]) > cdesc[HPMAX]) + cdesc[HP] = cdesc[HPMAX]; + break; + + case 2: + lprcat ("\nSuddenly, you feel much more skillful!"); + raiselevel (); + raisemhp (1); + return; + + case 3: + lprcat ("\nYou feel strange for a moment"); + cdesc[rund (6)]++; + break; + + case 4: + lprcat ("\nYou feel more self confident!"); + cdesc[WISDOM] += rnd (2); + break; + + case 5: + lprcat ("\nWow! You feel great!"); + if (cdesc[STRENGTH] < 12) + cdesc[STRENGTH] = 12; + else + cdesc[STRENGTH]++; + break; + + case 6: + lprcat ("\nYour charm went up by one!"); + cdesc[CHARISMA]++; + break; + + case 7: + lprcat ("\nYou become dizzy!"); + if (--cdesc[STRENGTH] < 3) + cdesc[STRENGTH] = 3; + break; + + case 8: + lprcat ("\nYour intelligence went up by one!"); + cdesc[INTELLIGENCE]++; + break; + + case 9: + lprcat ("\nYou sense the presence of objects!"); + nap (NAPTIME); + if (cdesc[BLINDCOUNT]) + return; + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + switch (item[j][i]) + { + case OPLATE: + case OCHAIN: + case OLEATHER: + case ORING: + case OSTUDLEATHER: + case OSPLINT: + case OPLATEARMOR: + case OSSPLATE: + case OSHIELD: + case OSWORDofSLASHING: + case OHAMMER: + case OSWORD: + case O2SWORD: + case OHSWORD: + case OSPEAR: + case ODAGGER: + case OBATTLEAXE: + case OLONGSWORD: + case OGREATSWORD: + case ORINGOFEXTRA: + case OREGENRING: + case OPROTRING: + case OENERGYRING: + case ODEXRING: + case OSTRRING: + case OCLEVERRING: + case ODAMRING: + case OBELT: + case OSCROLL: + case OPOTION: + case OBOOK: + case OCHEST: + case OAMULET: + case OORBOFDRAGON: + case OSPIRITSCARAB: + case OCUBEofUNDEAD: + case ONOTHEFT: + case OCOOKIE: + know[j][i] = HAVESEEN; + show1cell (j, i); + break; + } + showplayer (); + return; + + case 10: /* monster detection */ + lprcat ("\nYou detect the presence of monsters!"); + nap (NAPTIME); + if (cdesc[BLINDCOUNT]) + return; + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + if (mitem[j][i] && (monstnamelist[mitem[j][i]] != floorc)) + { + know[j][i] = HAVESEEN; + show1cell (j, i); + } + return; + + case 11: + lprcat ("\nYou stagger for a moment . ."); + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + know[j][i] = 0; + nap (1000); + draws (0, MAXX, 0, MAXY); /* potion of forgetfulness */ + return; + + case 12: + lprcat ("\nThis potion has no taste to it"); + return; + + case 13: + lprcat ("\nYou can't see anything!"); /* blindness */ + cdesc[BLINDCOUNT] += 500; + return; + + case 14: + lprcat ("\nYou feel confused"); + cdesc[CONFUSE] += 20 + rnd (9); + return; + + case 15: + lprcat ("\nWOW!!! You feel Super-fantastic!!!"); + if (cdesc[HERO] == 0) + for (i = 0; i < 6; i++) + cdesc[i] += 11; + cdesc[HERO] += 250; + break; + + case 16: + lprcat ("\nYou have a greater intestinal constitude!"); + cdesc[CONSTITUTION]++; + break; + + case 17: + lprcat ("\nYou now have incredibly bulging muscles!!!"); + if (cdesc[GIANTSTR] == 0) + cdesc[STREXTRA] += 21; + cdesc[GIANTSTR] += 700; + break; + + case 18: + lprcat ("\nYou feel a chill run up your spine!"); + cdesc[FIRERESISTANCE] += 1000; + break; + + case 19: + lprcat ("\nYou feel greedy . . ."); + nap (NAPTIME); + if (cdesc[BLINDCOUNT]) + return; + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + { + k = item[j][i]; + if ((k == ODIAMOND) || + (k == ORUBY) || + (k == OEMERALD) || + (k == OMAXGOLD) || + (k == OSAPPHIRE) || (k == OLARNEYE) || (k == OGOLDPILE)) + { + know[j][i] = HAVESEEN; + show1cell (j, i); + } + } + showplayer (); + return; + + case 20: + lprcat ("\nYou feel all better now!"); + cdesc[HP] = cdesc[HPMAX]; + break; /* instant healing */ + + case 21: + lprcat ("\nYou don't seem to be affected"); + return; /* cure dianthroritis */ + + case 22: + lprcat ("\nYou feel a sickness engulf you"); /* poison */ + cdesc[HALFDAM] += 200 + rnd (200); + return; + + case 23: + lprcat ("\nYou feel your vision sharpen"); /* see invisible */ + cdesc[SEEINVISIBLE] += rnd (1000) + 400; + monstnamelist[INVISIBLESTALKER] = 'I'; + return; + }; + bottomline (); /* show new stats */ + return; +} + + + +/* +* function to process a magic scroll +*/ +static void +oscroll (int typ) +{ + lprcat ("\nDo you "); + if (cdesc[BLINDCOUNT] == 0) + lprcat ("(r) read it, "); + lprcat ("(t) take it"); + iopts (); + for (;;) + { + switch (ttgetch ()) + { + case '\33': + case 'i': + ignore (); + return; + + case 'r': + if (cdesc[BLINDCOUNT]) + break; + lprcat ("read"); + forget (); + if (typ == 2 || typ == 15) + { + show1cell (playerx, playery); + cursors (); + } + /* destroy it */ read_scroll (typ); + return; + + case 't': + lprcat ("take"); + if (take (OSCROLL, typ) == 0) + forget (); /* destroy it */ + return; + }; + } +} + + + + + +/* +* data for the function to read a scroll +*/ +static int xh, yh, yl, xl; +static int curse[] = { BLINDCOUNT, CONFUSE, AGGRAVATE, HASTEMONST, ITCHING, + LAUGHING, DRAINSTRENGTH, CLUMSINESS, INFEEBLEMENT, + HALFDAM +}; + +static int exten[] = { PROTECTIONTIME, DEXCOUNT, STRCOUNT, CHARMCOUNT, + INVISIBILITY, CANCELLATION, HASTESELF, GLOBE, + SCAREMONST, HOLDMONST, TIMESTOP +}; + +static int time_change[] = + { HASTESELF, HERO, ALTPRO, PROTECTIONTIME, DEXCOUNT, + STRCOUNT, GIANTSTR, CHARMCOUNT, INVISIBILITY, + CANCELLATION, HASTESELF, AGGRAVATE, SCAREMONST, + STEALTH, AWARENESS, HOLDMONST, HASTEMONST, + FIRERESISTANCE, GLOBE, SPIRITPRO, UNDEADPRO, + HALFDAM, SEEINVISIBLE, ITCHING, CLUMSINESS, WTW +}; + + + +/* +* function to adjust time when time warping and taking courses in school +*/ +void +adjtimel (int tim) +{ + int j; + + for (j = 0; j < 26; j++) /* adjust time related parameters */ + if (cdesc[time_change[j]]) + if ((cdesc[time_change[j]] -= tim) < 1) + cdesc[time_change[j]] = 1; + + regen (); +} + + + +/* +* function to read a scroll +*/ +void +read_scroll (int typ) +{ + int i, j; + + if (typ < 0 || typ >= MAXSCROLL) + return; /* be sure we are within bounds */ + scrollname[typ][0] = ' '; + switch (typ) + { + case 0: + lprcat ("\nYour armor glows for a moment"); + enchantarmor (); + return; + + case 1: + lprcat ("\nYour weapon glows for a moment"); + enchweapon (); + return; /* enchant weapon */ + + case 2: + lprcat ("\nYou have been granted enlightenment!"); + yh = min (playery + 7, MAXY); + xh = min (playerx + 25, MAXX); + yl = max (playery - 7, 0); + xl = max (playerx - 25, 0); + for (i = yl; i < yh; i++) + for (j = xl; j < xh; j++) + know[j][i] = KNOWALL; + draws (xl, xh, yl, yh); + refresh(); + return; + + case 3: + lprcat ("\nThis scroll seems to be blank"); + return; + + case 4: + createmonster (makemonst (level + 1)); + return; /* this one creates a monster */ + + case 5: + something (level); /* create artifact */ + return; + + case 6: + lprcat ("\nSomething isn't right..."); + cdesc[AGGRAVATE] += 800; + return; /* aggravate monsters */ + + case 7: + gtime += (i = rnd (1000) - 850); /* time warp */ + if (i >= 0) + lprintf ("\nYou went forward in time by %d mobuls", + (int) ((i + 99) / 100)); + else + lprintf ("\nYou went backward in time by %d mobuls", + (int) (-(i + 99) / 100)); + adjtimel ((int) i); /* adjust time for time warping */ + return; + + case 8: + lprcat ("\nYour surroundings change"); + oteleport (0); + refresh(); + return; /* teleportation */ + + case 9: + lprcat ("\nYou feel extra alert"); + cdesc[AWARENESS] += 1800; + return; /* expanded awareness */ + + case 10: + lprcat ("\nSomething isn't right..."); + cdesc[HASTEMONST] += rnd (55) + 12; + return; /* haste monster */ + + case 11: + lprcat ("\nSomething isn't right..."); + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + if (mitem[j][i]) + hitp[j][i] = monster[mitem[j][i]].hitpoints; + return; /* monster healing */ + case 12: + cdesc[SPIRITPRO] += 300 + rnd (200); + bottomline (); + return; /* spirit protection */ + + case 13: + cdesc[UNDEADPRO] += 300 + rnd (200); + bottomline (); + return; /* undead protection */ + + case 14: + cdesc[STEALTH] += 250 + rnd (250); + bottomline (); + return; /* stealth */ + + case 15: + lprcat ("\nYou have been granted enlightenment!"); /* magic mapping */ + for (i = 0; i < MAXY; i++) + for (j = 0; j < MAXX; j++) + know[j][i] = KNOWALL; + draws (0, MAXX, 0, MAXY); + cdesc[TELEFLAG] = 0; /* magic map tells you what floor you're on */ + refresh(); + return; + + case 16: + cdesc[HOLDMONST] += 30; + bottomline (); + return; /* hold monster */ + + case 17: + lprcat ("\nYou feel someone eyeing your belongings"); + for (i = 0; i < 26; i++) /* gem perfection */ + switch (iven[i]) + { + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OSAPPHIRE: + j = ivenarg[i]; + j &= 255; + j <<= 1; + if (j > 255) + j = 255; /* double value */ + ivenarg[i] = j; + break; + } + break; + + case 18: + lprcat ("\nYou feel a twitch at the base of your skull"); + for (i = 0; i < 11; i++) + cdesc[exten[i]] <<= 1; /* spell extension */ + break; + + case 19: + lprcat ("\nYou feel someone eyeing your belongings"); + for (i = 0; i < 26; i++) /* identify */ + { + if (iven[i] == OPOTION) + potionname[ivenarg[i]][0] = ' '; + if (iven[i] == OSCROLL) + scrollname[ivenarg[i]][0] = ' '; + } + break; + + case 20: + lprcat ("\nYou sense a benign presence"); + for (i = 0; i < 10; i++) /* remove curse */ + if (cdesc[curse[i]]) + cdesc[curse[i]] = 1; + break; + + case 21: + annihilate (); + break; /* scroll of annihilation */ + + case 22: + godirect (22, 150, "The ray hits the %s", 0, ' '); /* pulverization */ + break; + case 23: + lprcat ("\nYou sense a benign presence"); + cdesc[LIFEPROT]++; + break; /* life protection */ + }; +} + +/* FIXES for bool bug */ +static void +opit (void) +{ + int i; + if (rnd (101) < 81) + { + if (rnd (70) > 9 * cdesc[DEXTERITY] - packweight () || rnd (101) < 5) + { + if (level == MAXLEVEL - 1) + obottomless (); + else if (level == MAXLEVEL + MAXVLEVEL - 1) + obottomless (); + else + { + if (rnd (101) < 20) + { + i = 0; + lprcat + ("\nYou fell into a pit! Your fall is cushioned by an unknown force\n"); + + } + else + { + i = rnd (level * 3 + 3); + lprintf + ("\nYou fell into a pit! You suffer %ld hit points damage", + (long) i); + lastnum = 261; /* if he dies scoreboard * will say so */ + + } + losehp (i); + nap (2000); + newcavelevel (level + 1); + draws (0, MAXX, 0, MAXY); + + } + + } + + } + +} + +static void +obottomless (void) +{ + lprcat ("\nYou fell into a bottomless pit!"); + nap (3000); + died (262); +} + + +static void +ostatue (void) +{ + +} + +/* I will add something here, for now it's a placeholder -Gibbon*/ +static void +omirror (void) +{ + lprcat ("\nMirror mirror on the wall.."); +} + +static void +obook (void) +{ + lprcat ("\nDo you "); + if (cdesc[BLINDCOUNT] == 0) + lprcat ("(r) read it, "); + lprcat ("(t) take it"); + iopts (); + for (;;) + { + switch (ttgetch ()) + { + case '\33': + case 'i': + ignore (); + return; + + case 'r': + if (cdesc[BLINDCOUNT]) + break; + lprcat ("read"); + /* no more book */ readbook (iarg[playerx][playery]); + forget (); + return; + + case 't': + lprcat ("take"); + if (take (OBOOK, iarg[playerx][playery]) == 0) + forget (); /* no more book */ + return; + }; + } +} + + + +/* +* function to read a book +*/ +void +readbook (int lev) +{ + int i, tmp; + + if (lev <= 3) + { + + tmp = splev[lev]; + if (tmp == 0) + tmp = 1; + + i = rund (tmp); + + } + else + { + + tmp = splev[lev] - 9; + if (tmp == 0) + tmp = 1; + + i = rnd (tmp + 9); + } + + spelknow[i] = 1; + + lprintf ("\nSpell \"%s\": %s\n%s", spelcode[i], spelname[i], + speldescript[i]); + + if (rnd (10) == 4) + { + + lprcat ("\nYour int went up by one!"); + cdesc[INTELLIGENCE]++; + bottomline (); + } +} + + + +static void +ocookie (void) +{ + + lprcat ("\nDo you (e) eat it, (t) take it"); + iopts (); + for (;;) + { + switch (ttgetch ()) + { + case '\33': + case 'i': + ignore (); + return; + + case 'e': + lprcat ("eat"); + forget (); /* no more cookie */ + outfortune (); + return; + + case 't': + lprcat ("take"); + if (take (OCOOKIE, 0) == 0) + forget (); /* no more book */ + return; + }; + } +} + + + +/* +* routine to pick up some gold -- if arg==OMAXGOLD then the pile is worth +* 100* the argument +*/ +static void +ogold (int arg) +{ + int i; + + i = iarg[playerx][playery]; + if (arg == OMAXGOLD) + i *= 100; + else if (arg == OKGOLD) + i *= 1000; + else if (arg == ODGOLD) + i *= 10; + lprintf ("\nIt is worth %d!", (int) i); + cdesc[GOLD] += i; + bottomgold (); + item[playerx][playery] = know[playerx][playery] = 0; /* destroy gold */ +} + + + +void +ohome (void) +{ + int i; + + + for (i = 0; i < 26; i++) + if (iven[i] == OPOTION) + if (ivenarg[i] == 21) + { + iven[i] = 0; /* remove the potion of cure + * dianthroritis from + * inventory */ + screen_clear(); + lprcat + ("Congratulations. You found a potion of cure dianthroritis.\n"); + lprcat + ("\nFrankly, No one thought you could do it. Boy! Did you surprise them!\n"); + if (gtime > TIMELIMIT) + { + lprcat + ("\nThe doctor has the sad duty to inform you that your daughter died"); + lprcat + ("\nbefore your return. There was nothing he could do without the potion.\n"); + nap (NAPTIME); + died (269); + } + else + { + lprcat + ("\nThe doctor is now administering the potion, and in a few moments\n"); + lprcat + ("your daughter should be well on her way to recovery.\n"); + nap (NAPTIME); + lprcat ("\nThe potion is"); + nap (NAPTIME); + lprcat (" working! The doctor thinks that\n"); + lprcat + ("your daughter will recover in a few days. Congratulations!\n"); + nap (NAPTIME); + died (263); + } + } + for (;;) + { + screen_clear(); + lprintf ("Welcome home %s. Latest word from the doctor is not good.\n", + logname); + + if (gtime > TIMELIMIT) + { + lprcat + ("\nThe doctor has the sad duty to inform you that your daughter died!\n"); + lprcat + ("You didn't make it in time. There was nothing he could do without the potion.\n"); + nap (NAPTIME); + died (269); + } + lprcat + ("\nThe diagnosis is confirmed as dianthroritis. He guesses that\n"); + lprintf + ("your daughter has only %d mobuls left in this world. It's up to you,\n", + ((TIMELIMIT - gtime + 99) / 100)); + lprintf ("%s, to find the only hope for your daughter, the very rare\n", + logname); + lprcat + ("potion of cure dianthroritis. It is rumored that only deep in the\n"); + lprcat ("depths of the caves can this potion be found.\n\n\n"); + lprcat ("\n ----- press "); + lstandout ("return"); + lprcat (" to continue, "); + lstandout ("escape"); + lprcat (" to leave ----- "); + i = ttgetch (); + while (i != '\33' && i != '\n') + i = ttgetch (); + drawscreen (); + return; + } +} + + + +/* routine to save program space */ +void +iopts (void) +{ + + lprcat (", or (i) ignore it? "); +} + + +void +ignore (void) +{ + + lprcat ("ignore\n"); +} + + + + + +/* +* For prompt mode, prompt for entering a building. +*/ +static void +prompt_enter (void) +{ + char i; + + lprcat ("\nDo you (g) go inside, or (i) stay here? "); + i = 0; + while ((i != 'g') && (i != 'i') && (i != '\33')) + i = ttgetch (); + if (i == 'g') + enter (); + else + lprcat (" stay here"); +} + + + + + +/* +* For prompt mode, prompt for climbing up/down the volcanic shaft. +* +* Takes one parameter: if it is negative, going down the shaft, otherwise, +* going up the shaft. +*/ +static void +prompt_volshaft (int dir) +{ + char i; + + lprcat ("\nDo you (c) climb "); + if (dir > 0) + lprcat ("up"); + else + lprcat ("down"); + iopts (); + + i = 0; + while ((i != 'c') && (i != 'i') && (i != '\33')) + i = ttgetch (); + + if ((i == '\33') || (i == 'i')) + { + ignore (); + return; + } + if (dir > 0) + act_up_shaft (); + else + act_down_shaft (); +} + + + + +static void +o_open_door (void) +{ + char i; + + lprcat ("\nDo you (c) close it"); + iopts (); + i = 0; + while ((i != 'c') && (i != 'i') && (i != '\33')) + i = ttgetch (); + if ((i == '\33') || (i == 'i')) + { + ignore (); + return; + } + lprcat ("close"); + forget (); + item[playerx][playery] = OCLOSEDDOOR; + iarg[playerx][playery] = 0; + playerx = lastpx; + playery = lastpy; +} + + + +static void +o_closed_door (void) +{ + char i; + + lprcat ("\nDo you (o) try to open it"); + iopts (); + i = 0; + while ((i != 'o') && (i != 'i') && (i != '\33')) + i = ttgetch (); + if ((i == '\33') || (i == 'i')) + { + ignore (); + playerx = lastpx; + playery = lastpy; + return; + } + else + { + lprcat ("open"); + /* + * if he failed to open the door ... + */ + if (!act_open_door (playerx, playery)) + { + playerx = lastpx; + playery = lastpy; + } + } +} diff --git a/src/regen.c b/src/regen.c new file mode 100644 index 0000000..6471990 --- /dev/null +++ b/src/regen.c @@ -0,0 +1,200 @@ +#include +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/inventory.h" +#include "includes/io.h" +#include "includes/regen.h" + +/* +* regen() +* +* subroutine to regenerate player hp and spells +*/ +void +regen (void) +{ + int i, flag; + long *d; + + d = cdesc; +#ifdef EXTRA + d[MOVESMADE]++; +#endif + if (d[TIMESTOP]) + { + if (--d[TIMESTOP] <= 0) + bottomline (); + return; + } /* for stop time spell */ + flag = 0; + + if (d[STRENGTH] < 3) + { + d[STRENGTH] = 3; + flag = 1; + } + if (d[HP] != d[HPMAX]) + if (d[REGENCOUNTER]-- <= 0) /* regenerate hit points */ + { + d[REGENCOUNTER] = 22 + (d[HARDGAME] << 1) - d[LEVEL]; + if ((d[HP] += d[REGEN]) > d[HPMAX]) + d[HP] = d[HPMAX]; + bottomhp (); + } + + if (d[SPELLS] < d[SPELLMAX]) /* regenerate spells */ + if (d[ECOUNTER]-- <= 0) + { + d[ECOUNTER] = 100 + 4 * (d[HARDGAME] - d[LEVEL] - d[ENERGY]); + d[SPELLS]++; + bottomspell (); + } + + if (d[HERO]) + if (--d[HERO] <= 0) + { + for (i = 0; i < 6; i++) + d[i] -= 10; + flag = 1; + } + if (d[ALTPRO]) + if (--d[ALTPRO] <= 0) + { + d[MOREDEFENSES] -= 3; + flag = 1; + } + if (d[PROTECTIONTIME]) + if (--d[PROTECTIONTIME] <= 0) + { + d[MOREDEFENSES] -= 2; + flag = 1; + } + if (d[DEXCOUNT]) + if (--d[DEXCOUNT] <= 0) + { + d[DEXTERITY] -= 3; + flag = 1; + } + if (d[STRCOUNT]) + if (--d[STRCOUNT] <= 0) + { + d[STREXTRA] -= 3; + flag = 1; + } + if (d[BLINDCOUNT]) + if (--d[BLINDCOUNT] <= 0) + { + cursors (); + lprcat ("\nThe blindness lifts "); + } + if (d[CONFUSE]) + if (--d[CONFUSE] <= 0) + { + cursors (); + lprcat ("\nYou regain your senses"); + } + if (d[GIANTSTR]) + if (--d[GIANTSTR] <= 0) + { + d[STREXTRA] -= 20; + flag = 1; + } + if (d[CHARMCOUNT]) + if ((--d[CHARMCOUNT]) <= 0) + flag = 1; + if (d[INVISIBILITY]) + if ((--d[INVISIBILITY]) <= 0) + flag = 1; + if (d[CANCELLATION]) + if ((--d[CANCELLATION]) <= 0) + flag = 1; + if (d[WTW]) + if ((--d[WTW]) <= 0) + flag = 1; + if (d[HASTESELF]) + if ((--d[HASTESELF]) <= 0) + flag = 1; + if (d[AGGRAVATE]) + --d[AGGRAVATE]; + if (d[SCAREMONST]) + if ((--d[SCAREMONST]) <= 0) + flag = 1; + if (d[STEALTH]) + if ((--d[STEALTH]) <= 0) + flag = 1; + if (d[AWARENESS]) + --d[AWARENESS]; + if (d[HOLDMONST]) + if ((--d[HOLDMONST]) <= 0) + flag = 1; + if (d[HASTEMONST]) + --d[HASTEMONST]; + if (d[FIRERESISTANCE]) + if ((--d[FIRERESISTANCE]) <= 0) + flag = 1; + if (d[GLOBE]) + if (--d[GLOBE] <= 0) + { + d[MOREDEFENSES] -= 10; + flag = 1; + } + if (d[SPIRITPRO]) + if (--d[SPIRITPRO] <= 0) + flag = 1; + if (d[UNDEADPRO]) + if (--d[UNDEADPRO] <= 0) + flag = 1; + if (d[HALFDAM]) + if (--d[HALFDAM] <= 0) + { + cursors (); + lprcat ("\nYou now feel better "); + } + if (d[SEEINVISIBLE]) + if (--d[SEEINVISIBLE] <= 0) + { + monstnamelist[INVISIBLESTALKER] = floorc; + if (!d[BLINDCOUNT]) + { + cursors (); + lprcat ("\nYou feel your vision return to normal"); + } + } + if (d[ITCHING]) + { + if (d[ITCHING] > 1) + if ((d[WEAR] != -1) || (d[SHIELD] != -1)) + if (rnd (100) < 50) + { + d[WEAR] = d[SHIELD] = -1; + cursors (); + lprcat + ("\nThe hysteria of itching forces you to remove your armor!"); + recalc (); + bottomline (); + } + if (--d[ITCHING] <= 0) + { + cursors (); + lprcat ("\nYou now feel the irritation subside!"); + } + } + if (d[CLUMSINESS]) + { + if (d[WIELD] != -1) + if (d[CLUMSINESS] > 1) + if (item[playerx][playery] == 0) /* only if nothing there */ + if (rnd (100) < 33) /* drop your weapon due to clumsiness */ + drop_object ((int) d[WIELD]); + if (--d[CLUMSINESS] <= 0) + { + cursors (); + lprcat ("\nYou now feel less awkward!"); + } + } + if (flag) + bottomline (); +} diff --git a/src/savelev.c b/src/savelev.c new file mode 100644 index 0000000..9e1dd88 --- /dev/null +++ b/src/savelev.c @@ -0,0 +1,82 @@ +/* savelev.c */ +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/savelev.h" + + + + +/* + * routine to save the present level into storage + */ +void +savelevel (void) +{ + struct cel *pcel; + int *pitem, *pknow, *pmitem; + int *phitp, *piarg; + struct cel *pecel; + + /* pointer to this level's cells */ + pcel = &cell[level * MAXX * MAXY]; + + /* pointer to past end of this level's cells */ + pecel = pcel + MAXX * MAXY; + + pitem = item[0]; + piarg = iarg[0]; + pknow = know[0]; + pmitem = mitem[0]; + phitp = hitp[0]; + + while (pcel < pecel) + { + + pcel->mitem = *pmitem++; + pcel->hitp = *phitp++; + pcel->item = *pitem++; + pcel->know = *pknow++; + pcel->iarg = *piarg++; + pcel++; + } +} + + + +/* + * routine to restore a level from storage + */ +void +getlevel (void) +{ + struct cel *pcel; + int *pitem; + int *pknow; + int *pmitem; + int *phitp, *piarg; + struct cel *pecel; + + /* pointer to this level's cells */ + pcel = &cell[level * MAXX * MAXY]; + + /* pointer to past end of this level's cells */ + pecel = pcel + MAXX * MAXY; + + pitem = item[0]; + piarg = iarg[0]; + pknow = know[0]; + pmitem = mitem[0]; + phitp = hitp[0]; + + while (pcel < pecel) + { + + *pmitem++ = pcel->mitem; + *phitp++ = pcel->hitp; + *pitem++ = pcel->item; + *pknow++ = pcel->know; + *piarg++ = pcel->iarg; + pcel++; + } +} diff --git a/src/scores.c b/src/scores.c new file mode 100644 index 0000000..c73810e --- /dev/null +++ b/src/scores.c @@ -0,0 +1,1031 @@ +/* scores.c +* +* readboard() Function to read in the scoreboard into a static buffer +* writeboard() Function to write the scoreboard from readboard()'s buffer +* makeboard() Function to create a new scoreboard (wipe out old one) +* hashewon() Function to return 1 if player has won a game before, else 0 +* int paytaxes(x) Function to pay taxes if any are due +* winshou() Subroutine to print out the winning scoreboard +* shou(x) Subroutine to print out the non-winners scoreboard +* showscores() Function to show the scoreboard on the terminal +* showallscores() Function to show scores and the iven lists that go with them +* sortboard() Function to sort the scoreboard +* newscore(score, whoo, whyded, winner) Function to add entry to scoreboard +* new1sub(score,i,whoo,taxes) Subroutine to put player into a +* new2sub(score,i,whoo,whyded) Subroutine to put player into a +* died(x) Subroutine to record who played larn, and what the score was +* diedsub(x) Subroutine to print out a line showing player when he is killed +* diedlog() Subroutine to read a log file and print it out in ascii format +* getplid(name) Function to get players id # from id file +*/ + +#if defined WINDOWS || WINDOWS_VS +#include +#endif + +#ifdef NIX +#include +#include +#endif + +#include +#include +#include +#include + +#include "includes/bill.h" +#include "includes/larn.h" +#include "includes/inventory.h" +#include "includes/scores.h" +#include "includes/sysdep.h" +#include "includes/io.h" + +struct scofmt /* This is the structure for the scoreboard */ +{ + int score; /* the score of the player */ + int what; /* the number of the monster that killed player */ + int level; /* the level player was on when he died */ + int hardlev; /* the level of difficulty player played at */ + int order; /* the relative ordering place of this entry */ + char who[LOGNAMESIZE]; /* the name of the character */ + int sciv[26][2]; /* this is the inventory list of the character */ +}; + +struct wscofmt /* This is the structure for the winning scoreboard */ +{ + int score; /* the score of the player */ + int timeused; /* the time used in mobuls to win the game */ + long taxes; /* taxes he owes to LRS */ + int hardlev; /* the level of difficulty player played at */ + int order; /* the relative ordering place of this entry */ + int hasmail; /* 1 if mail is to be read, 0 otherwise */ + char who[LOGNAMESIZE]; /* the name of the character */ +}; + +struct log_fmt /* 102 bytes struct for the log file */ +{ + long score; /* the players score */ + time_t diedtime; /* time when game was over */ + int cavelev; /* level in caves */ + int diff; /* difficulty player played at */ +#ifdef EXTRA + int elapsedtime; /* real time of game in seconds */ + int bytout; /* bytes input and output */ + int bytin; + int moves; /* number of moves made by player */ + int ac; /* armor class of player */ + int hp, hpmax; /* players hitpoints */ + int killed, spused; /* monsters killed and spells cast */ + int usage; /* usage of the cpu in % */ + int lev; /* player level */ +#endif + char who[12]; /* player name */ + char what[46]; /* what happened to player */ +}; + + +static int winshou (void); +static int shou (int); +static int sortboard (void); +static void newscore (int, char *, int, int); +static void new1sub (int, int, char *, int); +static void new2sub (int, int, char *, int); +static void diedsub (int); +static int readboard (void); +static int writeboard (void); +static struct scofmt sco[SCORESIZE]; /* the structure for the scoreboard */ +static struct wscofmt winr[SCORESIZE]; /* struct for the winning scoreboard */ + +static struct log_fmt logg; /* structure for the log file */ +static char *whydead[] = { + "quit", "suspended", "self - annihilated", "shot by an arrow", + "hit by a dart", "fell into a pit", "fell into a bottomless pit", + "a winner", "trapped in solid rock", "killed by a missing save file", + "killed by an old save file", "caught by the greedy cheater checker trap", + "killed by a protected save file", + "killed his family and committed suicide", + "erased by a wayward finger", "fell through a bottomless trap door", + "fell through a trap door", "drank some poisonous water", + "fried by an electric shock", "slipped on a volcano shaft", + "killed by a stupid act of frustration", "attacked by a revolting demon", + "hit by his own magic", "demolished by an unseen attacker", + "fell into the dreadful sleep", "killed by an exploding chest", + "teleported into solid rock", + /*26 */ "killed by a missing maze data file", "annihilated in a sphere", + "died a post mortem death", "wasted by a malloc() failure" +}; + + +/* Rewriting the scoring (read, write and make board functions) into plain 'ol C + because the previous implementations were shockingly awful, messy, + non-standard garbage. ~Gibbon +*/ + + +/* +* readboard() Function to read in the scoreboard into a static buffer +* +* returns -1 if unable to read in the scoreboard, returns 0 if all is OK +*/ +static int +readboard (void) +{ + FILE *pFile; + int b; + + pFile = fopen(scorefile, "rb"); + if (pFile == NULL) + { + printf("ERROR: scorefile is not readable \n"); + lflush(); + return(-1); + } + b = fread(sco,sizeof(struct scofmt)*SCORESIZE,1,pFile); + if (b != 1) + { + printf("ERROR: Loosers scoreboard is not readable\n"); + fclose(pFile); + return(-1); + } + b = fread(winr,sizeof(struct wscofmt)*SCORESIZE,1,pFile); + if (b != 1) + { + printf("ERROR: Winners scoreboard is not readable\n"); + fclose(pFile); + return(-1); + } + fclose(pFile); + return 0; +} + +/* +* writeboard() Function to write the scoreboard from readboard()'s buffer +* +* returns -1 if unable to write the scoreboard, returns 0 if all is OK +*/ +static int +writeboard (void) +{ + FILE *pFile; + int b; + set_score_output(); + + pFile = fopen(scorefile, "wb"); + if (pFile == NULL) + { + printf("ERROR: scorefile is not writable \n"); + lflush(); + return(-1); + } + b = fwrite(sco,sizeof(struct scofmt)*SCORESIZE,1,pFile); + if (b != 1) + { + printf("ERROR: Loosers scoreboard is not writable\n"); + fclose(pFile); + return(-1); + } + b = fwrite(winr,sizeof(struct wscofmt)*SCORESIZE,1,pFile); + if (b != 1) + { + printf("ERROR: Winners scoreboard is not writable\n"); + fclose(pFile); + return(-1); + } + fclose(pFile); + return 0; +} + +/* +* makeboard() Function to create a new scoreboard (wipe out old one) +* +* returns -1 if unable to write the scoreboard, returns 0 if all is OK +*/ +int +makeboard (void) +{ + int i; + for (i = 0; i < SCORESIZE; i++) + { + winr[i].taxes = winr[i].score = sco[i].score = 0; + winr[i].order = sco[i].order = (short) i; + } + if (writeboard()) + { + printf("ERROR: unable to write a new scoreboard\n"); + return(-1); + } +/* Why bother redefining a function? Just use standard *NIX functions + and be done with it. Windows won't even need to set permissions anyway + so no need for 'cross platform' here. Sheesh.. ~Gibbon +*/ +#if defined NIX + chmod(scorefile, 0666); +#endif + return(0); +} + +/* +* hashewon() Function to return 1 if player has won a game before, else 0 +* +* This function also sets cdesc[HARDGAME] to appropriate value -- 0 if not a +* winner, otherwise the next level of difficulty listed in the winners +* scoreboard. This function also sets outstanding_taxes to the value in +* the winners scoreboard. +*/ +int +hashewon (void) +{ + int i; + + cdesc[HARDGAME] = 0; + if (readboard () < 0) + return (0); /* can't find scoreboard */ + for (i = 0; i < SCORESIZE; i++) /* search through winners scoreboard */ + if (strcmp (winr[i].who, logname) == 0) + if (winr[i].score > 0) + { + cdesc[HARDGAME] = winr[i].hardlev + 1; + /* outstanding_taxes = winr[i].taxes; */ + return (1); + } + return (0); +} + + + +void +checkmail (void) +{ + int i; + int gold, taxes; + + if (readboard () < 0) + return; /* can't find scoreboard */ + for (i = 0; i < SCORESIZE; i++) /* search through winners scoreboard */ + if (strcmp (winr[i].who, logname) == 0 && winr[i].score > 0 + && winr[i].hasmail) + { + winr[i].hasmail = 0; + gold = taxes = winr[i].taxes; + writeboard (); + + /* Intuit the amount of gold -- should have changed + * the score file, but ... TAXRATE is an fraction. + */ + while ((gold * TAXRATE) < taxes) + gold += taxes; + readmail (gold); + } +} + + + +/* +* int paytaxes(x) Function to pay taxes if any are due +* +* Enter with the amount (in gp) to pay on the taxes. +* Returns amount actually paid. +*/ +int +paytaxes (int x) +{ + int i; + int amt; + + if (x < 0) + return (0L); + if (readboard () < 0) + return (0L); + for (i = 0; i < SCORESIZE; i++) + if (strcmp (winr[i].who, logname) == 0) /* look for players winning entry */ + if (winr[i].score > 0) /* search for a winning entry for the player */ + { + amt = winr[i].taxes; + if (x < amt) + amt = x; /* don't overpay taxes (Ughhhhh) */ + winr[i].taxes -= amt; + outstanding_taxes -= amt; + if (writeboard () < 0) + return (0); + return (amt); + } + return (0L); /* couldn't find user on winning scoreboard */ +} + + + +/* +* winshou() Subroutine to print out the winning scoreboard +* +* Returns the number of players on scoreboard that were shown +*/ +static int +winshou (void) +{ + struct wscofmt *p; + int i, j, count; + + for (count = j = i = 0; i < SCORESIZE; i++) /* is there anyone on the scoreboard? */ + if (winr[i].score != 0) + { + j++; + break; + } + if (j) + { + lprcat ("\n Score Difficulty Time Needed Larn Winners List\n"); + + for (i = 0; i < SCORESIZE; i++) /* this loop is needed to print out the */ + for (j = 0; j < SCORESIZE; j++) /* winners in order */ + { + p = &winr[j]; /* pointer to the scoreboard entry */ + if (p->order == i) + { + if (p->score) + { + count++; + lprintf ("%10d %2d %5d Mobuls %s \n", + (int) p->score, (int) p->hardlev, + (int) p->timeused, p->who); + } + break; + } + } + } + return (count); /* return number of people on scoreboard */ +} + + + +/* +* Subroutine to print out the non-winners scoreboard +* +* Enter with 0 to list the scores, enter with 1 to list inventories too +* Returns the number of players on scoreboard that were shown +*/ +static int +shou (int x) +{ + int i, j, n, k; + int count; + + for (count = j = i = 0; i < SCORESIZE; i++) /* is the scoreboard empty? */ + if (sco[i].score != 0) + { + j++; + break; + } + if (j) + { + lprcat ("\n Score Difficulty Larn Visitor Log\n"); + for (i = 0; i < SCORESIZE; i++) /* be sure to print them out in order */ + for (j = 0; j < SCORESIZE; j++) + if (sco[j].order == i) + { + if (sco[j].score) + { + count++; + lprintf ("%10ld %2d %s ", sco[j].score, + sco[j].hardlev, sco[j].who); + if (sco[j].what < 256) + lprintf ("killed by a %s", monster[sco[j].what].name); + else + lprintf ("%s", whydead[sco[j].what - 256]); + if (x != 263) + lprintf (" on %s", levelname[sco[j].level]); + if (x) + { + for (n = 0; n < 26; n++) + { + iven[n] = sco[j].sciv[n][0]; + ivenarg[n] = sco[j].sciv[n][1]; + } + for (k = 1; k < 99; k++) + { + for (n = 0; n < 26; n++) + { + if (k == iven[n]) + show3 (n); + } + } + lprcat ("\n\n"); + } + else + lprc ('\n'); + } + j = SCORESIZE; + } + } + return (count); /* return the number of players just shown */ +} + + + +/* +* showscores() Function to show the scoreboard on the terminal +* +* Returns nothing of value +*/ +static char esb[] = "The scoreboard is empty.\n"; + +void +showscores (void) +{ + int i, j; + + lflush (); + lcreat ((char *) 0); + + if (readboard () < 0) + { + + return; + } + + i = winshou (); + j = shou (0); + + if (i + j == 0) + { + + lprcat (esb); + + } + else + { + + lprc ('\n'); + } + + lflush (); +} + +/* showscores, clib version */ +/* +void show_scoreboard(void) +{ + int i, j; + FILE * fp; + + fp = fopen(scorefile, "rb"); + if (fp == NULL) { + puts("Can't read scoreboard\n"); + return; + } + fread(sco, sizeof(sco), 1, fp); + fread(winr, sizeof(winr), 1, fp); + fclose(fp); + + i = show_winscores(); + j = show_regscores(0); + + if (i + j == 0) + puts(esb); + else + putchar('\n'); +} +*/ + + +/* +* showallscores() Function to show scores and the iven lists that go with them +* +* Returns nothing of value +*/ +void +showallscores (void) +{ + int i, j; + + lflush (); + lcreat ((char *) 0); + if (readboard () < 0) + return; + cdesc[WEAR] = cdesc[WIELD] = cdesc[SHIELD] = -1; /* not wielding or wearing anything */ + for (i = 0; i < MAXPOTION; i++) + potionname[i][0] = ' '; + for (i = 0; i < MAXSCROLL; i++) + scrollname[i][0] = ' '; + i = winshou (); + j = shou (1); + if (i + j == 0) + lprcat (esb); + else + lprc ('\n'); + lflush (); +} + + + +/* +* sortboard() Function to sort the scoreboard +* +* Returns 0 if no sorting done, else returns 1 +*/ +static int +sortboard (void) +{ + int i, pos, j = 0; + int jdat; + + for (i = 0; i < SCORESIZE; i++) + sco[i].order = winr[i].order = -1; + pos = 0; + while (pos < SCORESIZE) + { + jdat = 0; + for (i = 0; i < SCORESIZE; i++) + if ((sco[i].order < 0) && (sco[i].score >= jdat)) + { + j = i; + jdat = sco[i].score; + } + sco[j].order = pos++; + } + pos = 0; + while (pos < SCORESIZE) + { + jdat = 0; + for (i = 0; i < SCORESIZE; i++) + if ((winr[i].order < 0) && (winr[i].score >= jdat)) + { + j = i; + jdat = winr[i].score; + } + winr[j].order = pos++; + } + return (1); +} + + + +/* +* newscore(score, whoo, whyded, winner) Function to add entry to scoreboard +* int score, winner, whyded; +* char *whoo; +* +* Enter with the total score in gp in score, players name in whoo, +* died() reason # in whyded, and TRUE/FALSE in winner if a winner +* ex. newscore(1000, "player 1", 32, 0); +*/ +static void +newscore (int score, char *whoo, int whyded, int winner) +{ + int i; + int taxes; + + if (readboard () < 0) + return; /* do the scoreboard */ + /* if a winner then delete all non-winning scores */ + if (cheat) + winner = 0; /* if he cheated, don't let him win */ + if (winner) + { + for (i = 0; i < SCORESIZE; i++) + if (strcmp (sco[i].who, logname) == 0) + sco[i].score = 0; + taxes = score * TAXRATE; + score += 100000 * cdesc[HARDGAME]; /* bonus for winning */ + /* if he has a slot on the winning scoreboard update it if greater score */ + for (i = 0; i < SCORESIZE; i++) + if (strcmp (winr[i].who, logname) == 0) + { + new1sub (score, i, whoo, taxes); + return; + } + /* he had no entry. look for last entry and see if he has a greater score */ + for (i = 0; i < SCORESIZE; i++) + if (winr[i].order == SCORESIZE - 1) + { + new1sub (score, i, whoo, taxes); + return; + } + } +/* for not winning scoreboard */ + else if (!cheat) +#if defined MULTIPLE_SCORE_ENTRY +/* a copy of the below score checking without the hard-coded limitation of a single entry (if the user wants it defined). Otherwise remove the define from the makefile. -Gibbon */ +{ + for (i = 0; i < SCORESIZE; i++) + if (sco[i].order == SCORESIZE - 1) + { + new2sub (score, i, whoo, whyded); + return; + } + } +} +#else + { + /* if he has a slot on the scoreboard update it if greater score.*/ + for (i = 0; i < SCORESIZE; i++) + if (strcmp (sco[i].who, logname) == 0) + { + new2sub (score, i, whoo, whyded); + return; + } +/* he had no entry. look for last entry and see if he has a greater score */ + for (i = 0; i < SCORESIZE; i++) + if (sco[i].order == SCORESIZE - 1) + { + new2sub (score, i, whoo, whyded); + return; + } + } +} +#endif + +/* +* new1sub(score,i,whoo,taxes) Subroutine to put player into a +* int score,i,whyded,taxes; winning scoreboard entry if his score +* char *whoo; is high enough +* +* Enter with the total score in gp in score, players name in whoo, +* died() reason # in whyded, and TRUE/FALSE in winner if a winner +* slot in scoreboard in i, and the tax bill in taxes. +* Returns nothing of value +*/ +static void +new1sub (int score, int i, char *whoo, int taxes) +{ + struct wscofmt *p; + + p = &winr[i]; + p->taxes += taxes; + if ((score >= p->score) || (cdesc[HARDGAME] > p->hardlev)) + { + strcpy (p->who, whoo); + p->score = score; + p->hardlev = cdesc[HARDGAME]; + p->timeused = gtime / 100; + p->hasmail = 1; + } +} + + + + +/* +* new2sub(score,i,whoo,whyded) Subroutine to put player into a +* int score,i,whyded,taxes; non-winning scoreboard entry if his +* char *whoo; score is high enough +* +* Enter with the total score in gp in score, players name in whoo, +* died() reason # in whyded, and slot in scoreboard in i. +* Returns nothing of value +*/ +static void +new2sub (int score, int i, char *whoo, int whyded) +{ + int j; + struct scofmt *p; + + p = &sco[i]; + if ((score >= p->score) || (cdesc[HARDGAME] > p->hardlev)) + { + strcpy (p->who, whoo); + p->score = score; + p->what = whyded; + p->hardlev = cdesc[HARDGAME]; + p->level = level; + for (j = 0; j < 26; j++) + { + p->sciv[j][0] = iven[j]; + p->sciv[j][1] = ivenarg[j]; + } + } +} + + + + +/* +* died(x) Subroutine to record who played larn, and what the score was +* int x; +* +* if x < 0 then don't show scores +* died() never returns! (unless cdesc[LIFEPROT] and a reincarnatable death!) +* +* < 256 killed by the monster number +* 256 quit +* 257 suspended +* 258 self - annihilated +* 259 shot by an arrow +* 260 hit by a dart +* 261 fell into a pit +* 262 fell into a bottomless pit +* 263 a winner +* 264 trapped in solid rock +* 265 killed by a missing save file +* 266 killed by an old save file +* 267 caught by the greedy cheater checker trap +* 268 killed by a protected save file +* 269 killed his family and killed himself +* 270 erased by a wayward finger +* 271 fell through a bottomless trap door +* 272 fell through a trap door +* 273 drank some poisonous water +* 274 fried by an electric shock +* 275 slipped on a volcano shaft +* 276 killed by a stupid act of frustration +* 277 attacked by a revolting demon +* 278 hit by his own magic +* 279 demolished by an unseen attacker +* 280 fell into the dreadful sleep +* 281 killed by an exploding chest +* 282 killed by a missing maze data file +* 283 killed by a sphere of annihilation +* 284 died a post mortem death +* 285 malloc() failure +* 300 quick quit -- don't put on scoreboard +*/ + +static int scorerror; + +void +died (int x) +{ + int f, win; + /*char ch, *mod; + time_t zzz; */ + + if (cdesc[LIFEPROT] > 0) /* if life protection */ + { + switch ((x > 0) ? x : -x) + { + case 256: + case 257: + case 262: + case 263: + case 265: + case 266: + case 267: + case 268: + case 269: + case 271: + case 282: + case 284: + case 285: + case 300: + goto invalid; /* can't be saved */ + }; + --cdesc[LIFEPROT]; + cdesc[HP] = cdesc[HPMAX]; + --cdesc[CONSTITUTION]; + cursors (); + lprcat ("\nYou feel wiiieeeeerrrrrd all over! "); + lflush (); + nap (3000); + + return; /* only case where died() returns */ + } + + cursors (); + lprcat ("\nPress any key to continue. "); + ttgetch (); + +invalid: + /*clearvt100(); */ + lflush (); + f = 0; + + /* if we are not to display the scores */ + if (x < 0) + { + f++; + x = -x; + } + + /* for quick exit or saved game */ + if ((x == 300) || (x == 257)) + { + clearvt100 (); + exit (EXIT_SUCCESS); + } + + if (x == 263) + win = 1; + else + win = 0; + + cdesc[GOLD] += cdesc[BANKACCOUNT]; + cdesc[BANKACCOUNT] = 0; + + /* now enter the player at the end of the scoreboard */ + newscore (cdesc[GOLD], logname, x, win); + diedsub (x); /* print out the score line */ + lflush (); + + set_score_output (); + if ((wizard == 0) && (cdesc[GOLD] > 0)) /* wizards can't score */ + { + /* now for the scoreboard maintenance -- not for a suspended game */ + if (x != 257) + { + if (sortboard ()) + scorerror = writeboard (); + } + } + if ((x == 256) || (x == 257) || (f != 0)) + { + clearvt100 (); + exit (EXIT_SUCCESS); + } + if (scorerror == 0) + { + lflush (); + screen_clear(); + resetscroll (); + showscores (); /* if we updated the scoreboard */ + cursors (); + lprcat ("\nPress any key to exit. "); + scbr (); + ttgetch (); + } + clearvt100 (); + exit (EXIT_SUCCESS); +} + + + +/* +* diedsub(x) Subroutine to print out the line showing the player when he is killed +* int x; +*/ +static void +diedsub (int x) +{ + char ch, *mod; + + lprintf ("Score: %ld, Diff: %ld, %s ", cdesc[GOLD], cdesc[HARDGAME], + logname); + + if (x < 256) + { + + ch = *monster[x].name; + + if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') + { + + mod = "an"; + + } + else + { + + mod = "a"; + } + + lprintf ("killed by %s %s", mod, monster[x].name); + + } + else + { + + lprintf ("%s", whydead[x - 256]); + } + + if (x != 263) + { + + lprintf (" on %s\n", levelname[level]); + + } + else + { + + lprc ('\n'); + } +} + + + +/* +* diedlog() Subroutine to read a log file and print it out in ascii format +*/ +void +diedlog (void) +{ + int n; + char *p; + struct stat stbuf; + + lcreat ((char *) 0); + if (lopen (logfile) < 0) + { + lprintf ("Can't locate log file <%s>\n", logfile); + return; + } + if (fstat (fd, &stbuf) < 0) + { + lprintf ("Can't stat log file <%s>\n", logfile); + return; + } + for (n = stbuf.st_size / sizeof (struct log_fmt); n > 0; --n) + { + lrfill ((char *) &logg, sizeof (struct log_fmt)); + p = ctime ((time_t *) & logg.diedtime); + p[16] = '\n'; + p[17] = 0; + lprintf ("Score: %d, Diff: %d, %s %s on %d at %s", (int) (logg.score), + (int) (logg.diff), logg.who, logg.what, (int) (logg.cavelev), + p + 4); +#ifdef EXTRA + if (logg.moves <= 0) + logg.moves = 1; + lprintf + (" Experience Level: %d, AC: %d, HP: %d/%d, Elapsed Time: %d minutes\n", + (int) (logg.lev), (int) (logg.ac), (int) (logg.hp), + (int) (logg.hpmax), (int) (logg.elapsedtime)); + + lprintf + (" BYTES in: %d, out: %d, moves: %d, deaths: %d, spells cast: %d\n", + (int) (logg.bytin), (int) (logg.bytout), (int) (logg.moves), + (int) (logg.killed), (int) (logg.spused)); + lprintf (" out bytes per move: %d", (int) (logg.bytout / logg.moves)); + lprintf ("\n"); +#endif + } + lflush (); + lrclose (); + return; +} + + + + +/* +* getplid(name) Function to get players id # from id file +* +* Enter with the name of the players character in name. +* Returns the id # of the players character, or -1 if failure. +* This routine will try to find the name in the id file, if its not there, +* it will try to make a new entry in the file. Only returns -1 if can't +* find him in the file, and can't make a new entry in the file. +* Format of playerids file: +* Id # in ascii \n character name \n +*/ +static int havepid = -1; /* playerid # if previously done */ + +int +getplid (char *nam) +{ + int fd7, high = 999, no; + char *p, *p2; + char name[80]; + + if (havepid != -1) + return (havepid); /* already did it */ + lflush (); /* flush any pending I/O */ + sprintf (name, "%s\n", nam); /* append a \n to name */ + if (lopen (playerids) < 0) /* no file, make it */ + { +#if defined WINDOWS + if ((fd7 = _open(playerids, _S_IWUSR)) < 0) + { + return (-1); /* can't make it */ + } + _close (fd7); +#endif +#if defined WINDOWS_VS + if ((fd7 = _open(playerids, _S_IREAD)) < 0) + { + return (-1); /* can't make it */ + } + _close(fd7); +#endif +#if defined NIX + if ((fd7 = open(playerids, S_IWUSR)) < 0) + { + return (-1); /* can't make it */ + } + close (fd7); +#endif + goto addone; /* now append new playerid record to file */ + } + for (;;) /* now search for the name in the player id file */ + { + p = lgetl (); + if (p == NULL) + break; /* EOF? */ + no = atoi (p); /* the id # */ + p2 = lgetl (); + if (p2 == NULL) + break; /* EOF? */ + if (no > high) + high = no; /* accumulate highest id # */ + if (strcmp (p2, name) == 0) /* we found him */ + { + return (no); /* his id number */ + } + } + lrclose (); + /* if we get here, we didn't find him in the file -- put him there */ +addone: + if (lappend (playerids) < 0) + return (-1); /* can't open file for append */ + lprintf ("%d\n%s", (int) ++high, name); /* new id # and name */ + lwclose (); + lcreat ((char *) 0); /* re-open terminal channel */ + return (high); +} diff --git a/src/spells.c b/src/spells.c new file mode 100644 index 0000000..976b1eb --- /dev/null +++ b/src/spells.c @@ -0,0 +1,1354 @@ +/* +cast() Subroutine called by parse to cast a spell for the user +speldamage(x) Function to perform spell functions cast by the player +loseint() Routine to decrement your int (intelligence) if > 3 +isconfuse() Routine to check to see if player is confused +nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster +fullhit(xx) Function to return full damage against a monst (aka web) +direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir +godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks +ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt +tdirect(spnum) Routine to teleport away a monster +omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player +dirsub(x,y) Routine to ask for direction, then modify x,y for it +dirpoly(spnum) Routine to ask for a direction and polymorph a monst +annihilate() Routine to annihilate monsters around player, playerx,playery +genmonst() Function to ask for monster and genocide from game +*/ +#include +#include +#include +#include + +#include "includes/create.h" +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/io.h" +#include "includes/main.h" +#include "includes/monster.h" +#include "includes/object.h" +#include "includes/scores.h" +#include "includes/spells.h" +#include "includes/spheres.h" +#include "includes/sysdep.h" + +/* used for altar reality */ +struct isave +{ + + int type; /* 0=item, 1=monster */ + int id; /* item number or monster number */ + + int arg; /* the type of item or hitpoints of monster */ +}; + + + +static void speldamage (int); + +static void create_guardian (int, int, int); + +static void loseint (void); + +static int isconfuse (void); + +static int nospell (int, int); + +static void direct (int, int, char *, int); + +static void tdirect (int); + +static void omnidirect (int, int, char *); + +static void dirpoly (int); + +static void genmonst (void); + + + +/* +* cast() Subroutine called by parse to cast a spell for the user +* +* No arguments and no return value. +*/ +static char eys[] = "\nEnter your spell: "; + +void +cast (void) +{ + int i, j, a, b, d; + + cursors (); + + if (cdesc[SPELLS] <= 0) + { + + lprcat ("\nYou don't have any spells!"); + + return; + } + + lprcat (eys); + + --cdesc[SPELLS]; + + while ((a = ttgetch ()) == 'I') + { + + seemagic (-1); + cursors (); + lprcat (eys); + } + + if (a == '\33') + goto over; /* to escape casting a spell */ + if ((b = ttgetch ()) == '\33') + goto over; /* to escape casting a spell */ + if ((d = ttgetch ()) == '\33') + { + over: + lprcat (aborted); + cdesc[SPELLS]++; + return; + } /* to escape casting a spell */ +#ifdef EXTRA + cdesc[SPELLSCAST]++; +#endif + for (lprc ('\n'), j = -1, i = 0; i < SPNUM; i++) /*seq search for his spell, hash? */ + if ((spelcode[i][0] == a) && (spelcode[i][1] == b) + && (spelcode[i][2] == d)) + if (spelknow[i]) + { + speldamage (i); + j = 1; + i = SPNUM; + } + + if (j == -1) + lprcat (" Nothing Happened "); + bottomline (); +} + + + +/* +* speldamage(x) Function to perform spell functions cast by the player +* int x; +* +* Enter with the spell number, returns no value. +* Please insure that there are 2 spaces before all messages here +*/ +static void +speldamage (int x) +{ + int i, j, clev; + int xl, xh, yl, yh; + int *kn, *pm, *p; + + if (x >= SPNUM) + return; /* no such spell */ + if (cdesc[TIMESTOP]) + { + lprcat (" It didn't seem to work"); + return; + } /* not if time stopped */ + clev = cdesc[LEVEL]; + if ((rnd (23) == 7) || (rnd (18) > cdesc[INTELLIGENCE])) + { + lprcat (" It didn't work!"); + return; + } + if (clev * 3 + 2 < x) + { + lprcat (" Nothing happens. You seem inexperienced at this"); + return; + } + + switch (x) + { + /* ----- LEVEL 1 SPELLS ----- */ + + case 0: + if (cdesc[PROTECTIONTIME] == 0) + cdesc[MOREDEFENSES] += 2; /* protection field +2 */ + cdesc[PROTECTIONTIME] += 250; + return; + + case 1: + i = rnd (((clev + 1) << 1)) + clev + 3; + godirect (x, i, (clev >= 2) ? " Your missiles hit the %s" : " Your missile hit the %s", 100, '+'); /* magic missile */ + + return; + + case 2: + if (cdesc[DEXCOUNT] == 0) + cdesc[DEXTERITY] += 3; /* dexterity */ + cdesc[DEXCOUNT] += 400; + return; + + /*Further fixes below for issue #36. Removed crusty old 'C' and replaced with + direct function calls. -Gibbon*/ + case 3: + i = rnd (3) + 1; + /*Fix for bug #24 added newlines to the 'msg' for web and sleep spells. + Removed the msg and used lprcat instead plus color. ~Gibbon*/ + direct (x, fullhit (i), "\nwhile the %s slept, you hit %d times ", i); /* sleep */ + return; + + case 4: /* charm monster */ + cdesc[CHARMCOUNT] += cdesc[CHARISMA] << 1; + return; + + case 5: + godirect (x, rnd (10) + 15 + clev, " The sound damages the %s", 70, '@'); /* sonic spear */ + return; + + + /* ----- LEVEL 2 SPELLS ----- */ + + case 6: + i = rnd (3) + 2; + direct (x, fullhit (i),"\nYou damage the %s and hit %d times ", i); /* web */ + return; + + case 7: + if (cdesc[STRCOUNT] == 0) + cdesc[STREXTRA] += 3; /* strength */ + cdesc[STRCOUNT] += 150 + rnd (100); + return; + + case 8: + yl = playery - 5; /* enlightenment */ + yh = playery + 6; + xl = playerx - 15; + xh = playerx + 16; + vxy (&xl, &yl); + vxy (&xh, &yh); /* check bounds */ + for (i = yl; i <= yh; i++) /* enlightenment */ + for (j = xl; j <= xh; j++) + know[j][i] = KNOWALL; + draws (xl, xh + 1, yl, yh + 1); + return; + + case 9: + raisehp (20 + (clev << 1)); + return; /* healing */ + + case 10: + cdesc[BLINDCOUNT] = 0; + return; /* cure blindness */ + + case 11: + createmonster (makemonst (level + 1) + 8); + return; + + case 12: + if (rnd (11) + 7 <= cdesc[WISDOM]) + direct (x, rnd (20) + 20 + clev, "\nThe %s believed!", 0); + else + lprcat ("\n It didn't believe the illusions!"); + return; + + case 13: /* if he has the amulet of invisibility then add more time */ + for (j = i = 0; i < 26; i++) + if (iven[i] == OAMULET) + j += 1 + ivenarg[i]; + cdesc[INVISIBILITY] += (j << 7) + 12; + return; + + /* ----- LEVEL 3 SPELLS ----- */ + + case 14: + godirect (x, rnd (25 + clev) + 25 + clev, "\nThe fireball hits the %s", + 40, '*'); + return; /* fireball */ + + case 15: + godirect (x, rnd (25) + 20 + clev, "\nYour cone of cold strikes the %s", 60, 'O'); /* cold */ + return; + + case 16: + dirpoly (x); + return; /* polymorph */ + + case 17: + cdesc[CANCELLATION] += 5 + clev; + return; /* cancellation */ + + case 18: + cdesc[HASTESELF] += 7 + clev; + return; /* haste self */ + + case 19: + omnidirect (x, 30 + rnd (10), "\nThe %s gasps for air"); /* cloud kill */ + return; + + case 20: + xh = min (playerx + 1, MAXX - 2); + yh = min (playery + 1, MAXY - 2); + for (i = max (playerx - 1, 1); i <= xh; i++) /* vaporize rock */ + for (j = max (playery - 1, 1); j <= yh; j++) + { + kn = &know[i][j]; + pm = &mitem[i][j]; + switch (*(p = &item[i][j])) + { + case OWALL: + if (level < MAXLEVEL + MAXVLEVEL - 1) + *p = *kn = 0; + break; + + case OSTATUE: + if (cdesc[HARDGAME] < 3) + { + *p = OBOOK; + iarg[i][j] = level; + *kn = 0; + } + break; + + case OTHRONE: + *p = OTHRONE2; + create_guardian (GNOMEKING, i, j); + break; + + case OALTAR: + create_guardian (DEMONPRINCE, i, j); + break; + + case OFOUNTAIN: + create_guardian (WATERLORD, i, j); + break; + }; + switch (*pm) + { + case XORN: + ifblind (i, j); + hitm (i, j, 200); + break; /* Xorn takes damage from vpr */ + } + } + return; + + /* ----- LEVEL 4 SPELLS ----- */ + + case 21: + direct (x, 100 + clev, "\nThe %s shrivels up", 0); /* dehydration */ + return; + + case 22: + godirect (x, rnd (25) + 20 + (clev << 1), "\nA lightning bolt hits the %s", 1, '~'); /* lightning */ + return; + + case 23: + i = min (cdesc[HP] - 1, cdesc[HPMAX] / 2); /* drain life */ + direct (x, i + i, "", 0); + cdesc[HP] -= i; + return; + + case 24: + if (cdesc[GLOBE] == 0) + cdesc[MOREDEFENSES] += 10; + cdesc[GLOBE] += 200; + loseint (); /* globe of invulnerability */ + return; + + case 25: + omnidirect (x, 32 + clev, "\nThe %s struggles for air in your flood!"); /* flood */ + return; + + case 26: + if (rnd (151) == 63) + { + lprcat ("\nYour heart stopped!\n"); + nap (NAPTIME); + died (270); + return; + } + if (cdesc[WISDOM] > rnd (10) + 10) + direct (x, 2000, " \nThe %s's heart stopped", 0); /* finger of death */ + else + lprcat (" It didn't work"); + return; + + /* ----- LEVEL 5 SPELLS ----- */ + + case 27: + cdesc[SCAREMONST] += rnd (10) + clev; + return; /* scare monster */ + + case 28: + cdesc[HOLDMONST] += rnd (10) + clev; + return; /* hold monster */ + + case 29: + cdesc[TIMESTOP] += rnd (20) + (clev << 1); + return; /* time stop */ + + case 30: + tdirect (x); + return; /* teleport away */ + + case 31: + omnidirect (x, 35 + rnd (10) + clev, "\nThe %s cringes from the flame"); /* magic fire */ + return; + + /* ----- LEVEL 6 SPELLS ----- */ + + case 32: + if ((rnd (23) == 5) && (wizard == 0)) /* sphere of annihilation */ + { + lprcat ("\n You have been enveloped by the zone of nothingness!\n"); + nap (NAPTIME); + died (258); + return; + } + xl = playerx; + yl = playery; + loseint (); + i = dirsub (&xl, &yl); /* get direction of sphere */ + newsphere (xl, yl, i, rnd (20) + 11); /* make a sphere */ + return; + + case 33: + genmonst (); + spelknow[33] = 0; /* genocide */ + loseint (); + return; + + case 34: /* summon demon */ + if (rnd (100) > 30) + { + direct (x, 150, "\n The demon strikes at the %s", 0); + return; + } + if (rnd (100) > 15) + { + lprcat (" Nothing seems to have happened"); + return; + } + lprcat (" The"); + attron(COLOR_PAIR(2)); + lprcat(" demon "); + attroff(COLOR_PAIR(2)); + lprcat("turned on you and vanished!"); + i = rnd (40) + 30; + lastnum = 277; + losehp (i); /* must say killed by a demon */ + return; + + case 35: /* walk through walls */ + cdesc[WTW] += rnd (10) + 5; + return; + + case 36: /* alter reality */ + { + struct isave *save; /* pointer to item save structure */ + int sc; + sc = 0; /* # items saved */ + save = + (struct isave *) malloc (sizeof (struct isave) * MAXX * MAXY * 2); + if (save == NULL) + { + lprcat ("\n Polinneaus won't let you mess with his dungeon!"); + return; + } + for (j = 0; j < MAXY; j++) + for (i = 0; i < MAXX; i++) /* save all items and monsters */ + { + xl = item[i][j]; + if (xl && xl != OWALL && xl != OANNIHILATION) + { + save[sc].type = 0; + save[sc].id = item[i][j]; + save[sc++].arg = iarg[i][j]; + } + if (mitem[i][j]) + { + save[sc].type = 1; + save[sc].id = mitem[i][j]; + save[sc++].arg = hitp[i][j]; + } + item[i][j] = OWALL; + mitem[i][j] = 0; + if (wizard) + know[i][j] = KNOWALL; + else + know[i][j] = 0; + } + eat (1, 1); + if (level == 1) + item[33][MAXY - 1] = OENTRANCE; + for (j = rnd (MAXY - 2), i = 1; i < MAXX - 1; i++) + item[i][j] = 0; + while (sc > 0) /* put objects back in level */ + { + --sc; + if (save[sc].type == 0) + { + int trys; + for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; + i = rnd (MAXX - 1), j = rnd (MAXY - 1)); + if (trys) + { + item[i][j] = save[sc].id; + iarg[i][j] = save[sc].arg; + } + } + else + { /* put monsters back in */ + int trys; + for (trys = 100, i = j = 1; + --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); + i = rnd (MAXX - 1), j = rnd (MAXY - 1)); + if (trys) + { + mitem[i][j] = save[sc].id; + hitp[i][j] = save[sc].arg; + } + } + } + loseint (); + draws (0, MAXX, 0, MAXY); + if (wizard == 0) + spelknow[36] = 0; + free ((char *) save); + positionplayer (); + return; + } + + case 37: /* permanence */ + adjtimel (-99999L); + spelknow[37] = 0; /* forget */ + loseint (); + return; + + default: + lprintf ("spell %d not available!", (int) x); + return; + }; +} + + + +/* +* Create a guardian for a throne/altar/fountain, as a result of the player +* using a VPR spell or pulverization scroll on it. +* +* monst = monster code for guardian +* x, y = coords of the object being guarded +* +*/ +static void +create_guardian (int monst, int x, int y) +{ + int k; + + /* prevent the guardian from being created on top of the player */ + if (x == playerx && y == playery) + { + + k = rnd (8); + + x += diroffx[k]; + y += diroffy[k]; + } + + know[x][y] = 0; + mitem[x][y] = monst; + hitp[x][y] = monster[monst].hitpoints; +} + + + +/* +* loseint() Routine to subtract 1 from your int (intelligence) if > 3 +* +* No arguments and no return value +*/ +static void +loseint (void) +{ + + if (--cdesc[INTELLIGENCE] < 3) + { + + cdesc[INTELLIGENCE] = 3; + } +} + + + +/* +* isconfuse() Routine to check to see if player is confused +* +* This routine prints out a message saying "You can't aim your magic!" +* returns 0 if not confused, non-zero (time remaining confused) if confused +*/ +static int +isconfuse (void) +{ + if (cdesc[CONFUSE]) + { + lprcat (" You can't aim your magic!"); + } + + return (cdesc[CONFUSE]); +} + + + +/* +* nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster +* int x,monst; +* +* Subroutine to return 1 if the spell can't affect the monster +* otherwise returns 0 +* Enter with the spell number in x, and the monster number in monst. +*/ +static int +nospell (int x, int monst) +{ + int tmp; + + if (x >= SPNUM || monst >= MAXMONST + 8 || monst < 0 || x < 0) + return (0); /* bad spell or monst */ + if ((tmp = spelweird[monst - 1][x]) == 0) + return (0); + cursors (); + lprc ('\n'); + lprintf (spelmes[tmp], monster[monst].name); + return (1); +} + + + + +/* +* fullhit(xx) Function to return full damage against a monster (aka web) +* int xx; +* +* Function to return hp damage to monster due to a number of full hits +* Enter with the number of full hits being done +*/ +int +fullhit (int xx) +{ + int i; + + if (xx < 0 || xx > 20) + return (0); /* fullhits are out of range */ + if (cdesc[GREATSWORDDEATH]) + return (10000); /* great sword of death */ + i = xx * ((cdesc[WCLASS] >> 1) + cdesc[STRENGTH] + cdesc[STREXTRA] - + cdesc[HARDGAME] - 12 + cdesc[MOREDAM]); + return ((i >= 1) ? i : xx); +} + + + + +/* +* direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir +* int spnum,dam,arg; +* char *str; +* +* Routine to ask for a direction to a spell and then hit the monster +* Enter with the spell number in spnum, the damage to be done in dam, +* lprintf format string in str, and lprintf's argument in arg. +* Returns no value. +*/ +static void +direct (int spnum, int dam, char *str, int arg) +{ + int x, y; + int m; + + /* bad arguments */ + if (spnum < 0 || spnum >= SPNUM || str == NULL) + { + + return; + } + + if (isconfuse ()) + { + + return; + } + + dirsub (&x, &y); + + m = mitem[x][y]; + + if (item[x][y] == OMIRROR) + { + if (spnum == 3) /* sleep */ + { + lprcat ("You fall asleep! "); + fool: + arg += 2; + while (arg-- > 0) + { + parse2 (); + nap (NAPTIME); + } + return; + } + else if (spnum == 6) /* web */ + { + lprcat ("You get stuck in your own web! "); + goto fool; + } + else + { + lastnum = 278; + lprintf (str, "spell caster (thats you)", (int) arg); + losehp (dam); + return; + } + } + if (m == 0) + { + lprcat (" There wasn't anything there!"); + return; + } + ifblind (x, y); + if (nospell (spnum, m)) + { + lasthx = x; + lasthy = y; + return; + } + lprintf (str, lastmonst, (int) arg); + hitm (x, y, dam); +} + + + +/* +* godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks +* int spnum,dam,delay; +* char *str,cshow; +* +* Function to hit in a direction from a missile weapon and have it keep +* on going in that direction until its power is exhausted +* Enter with the spell number in spnum, the power of the weapon in hp, +* lprintf format string in str, the # of milliseconds to delay between +* locations in delay, and the character to represent the weapon in cshow. +* Returns no value. +*/ +void +godirect (int spnum, int dam, char *str, int delay, char cshow) +{ + int *p; + int x, y, m; + int dx, dy; + + /* bad args */ + if (spnum < 0 || spnum >= SPNUM || str == 0 || delay < 0) + { + + return; + } + + if (isconfuse ()) + { + + return; + } + + dirsub (&dx, &dy); + + x = dx; + y = dy; + + dx = x - playerx; + dy = y - playery; + + x = playerx; + y = playery; + + while (dam > 0) + { + + x += dx; + y += dy; + + if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) + { + + dam = 0; + + /* out of bounds */ + break; + } + + /* if energy hits player */ + if ((x == playerx) && (y == playery)) + { + + cursors (); + lprcat ("\nYou are hit by your own magic!"); + lastnum = 278; + losehp (dam); + + return; + } + + /* if not blind show effect */ + if (cdesc[BLINDCOUNT] == 0) + { + + cursor (x + 1, y + 1); + lprc (cshow); + nap (delay); + show1cell (x, y); + } + + m = mitem[x][y]; + + /* is there a monster there? */ + if (m != 0) + { + + ifblind (x, y); + + if (nospell (spnum, m)) + { + + lasthx = x; + lasthy = y; + + return; + } + + cursors (); + lprc ('\n'); + lprintf (str, lastmonst); + dam -= hitm (x, y, dam); + show1cell (x, y); + nap (NAPTIME); + + x -= dx; + y -= dy; + } + else + + switch (*(p = &item[x][y])) + { + + case OWALL: + cursors (); + lprc ('\n'); + lprintf (str, "wall"); + if ( + /* enough damage? */ + dam >= 50 + cdesc[HARDGAME] && + /* not on V3 */ + level < MAXLEVEL + MAXVLEVEL - 1 && + x < MAXX - 1 && y < MAXY - 1 && x != 0 && y != 0) + { + + lprcat (" The wall crumbles"); + *p = 0; + know[x][y] = 0; + show1cell (x, y); + } + + dam = 0; + break; + + + case OCLOSEDDOOR: + + cursors (); + lprc ('\n'); + lprintf (str, "door"); + + if (dam >= 40) + { + lprcat (" The door is blasted apart"); + *p = 0; + know[x][y] = 0; + show1cell (x, y); + } + + dam = 0; + break; + + case OSTATUE: + + cursors (); + lprc ('\n'); + lprintf (str, "statue"); + + if (cdesc[HARDGAME] < 3) + if (dam > 44) + { + lprcat (" The statue crumbles"); + *p = OBOOK; + iarg[x][y] = level; + know[x][y] = 0; + show1cell (x, y); + } + dam = 0; + break; + + case OTHRONE: + cursors (); + lprc ('\n'); + lprintf (str, "throne"); + if (dam > 39) + { + *p = OTHRONE2; + create_guardian (GNOMEKING, x, y); + show1cell (x, y); + } + dam = 0; + break; + + case OALTAR: + cursors (); + lprc ('\n'); + lprintf (str, "altar"); + if (dam > 75 - (cdesc[HARDGAME] >> 2)) + { + create_guardian (DEMONPRINCE, x, y); + show1cell (x, y); + } + dam = 0; + break; + + case OFOUNTAIN: + cursors (); + lprc ('\n'); + lprintf (str, "fountain"); + if (dam > 55) + { + create_guardian (WATERLORD, x, y); + show1cell (x, y); + } + dam = 0; + break; + + case OMIRROR: + { + int bounce = FALSE, odx = dx, ody = dy; + + /* spells may bounce directly back or off at an angle */ + if (rnd (100) < 50) + { + + bounce = TRUE; + dx *= -1; + } + + if (rnd (100) < 50) + { + + bounce = TRUE; + dy *= -1; + } + + /* guarentee a bounce */ + if (!bounce || (odx == dx && ody == dy)) + { + + dx = -odx; + dy = -ody; + } + } + break; + + }; + + dam -= 3 + (cdesc[HARDGAME] >> 1); + } +} + + + +/* +* ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt +* int x,y; +* +* Subroutine to copy the word "monster" into lastmonst if the player is blind +* Enter with the coordinates (x,y) of the monster +* Returns no value. +*/ +void +ifblind (int x, int y) +{ + char *p; + + /* verify correct x, y coordinates */ + vxy (&x, &y); + + if (cdesc[BLINDCOUNT]) + { + + lastnum = 279; + p = "monster"; + + } + else + { + + lastnum = mitem[x][y]; + p = monster[lastnum].name; + } + + strcpy (lastmonst, p); +} + + + +/* +* tdirect(spnum) Routine to teleport away a monster +* int spnum; +* +* Routine to ask for a direction to a spell and then teleport away monster +* Enter with the spell number that wants to teleport away +* Returns no value. +*/ +static void +tdirect (int spnum) +{ + int x, y, m; + + /* bad args */ + if (spnum < 0 || spnum >= SPNUM) + { + + return; + } + + if (isconfuse ()) + { + + return; + } + + dirsub (&x, &y); + + m = mitem[x][y]; + + if (m == 0) + { + + lprcat (" There wasn't anything there!"); + + return; + } + + ifblind (x, y); + + if (nospell (spnum, m)) + { + + lasthx = x; + lasthy = y; + + return; + } + + fillmonst (m); + + mitem[x][y] = 0; + know[x][y] &= ~KNOWHERE; +} + + + +/* +* omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player +* int sp,dam; +* char *str; +* +* Routine to cast a spell and then hit the monster in all directions +* Enter with the spell number in sp, the damage done to wach square in dam, +* and the lprintf string to identify the spell in str. +* Returns no value. +*/ +static void +omnidirect (int spnum, int dam, char *str) +{ + int x, y, m; + + /* bad args */ + if (spnum < 0 || spnum >= SPNUM || str == 0) + { + + return; + } + + for (x = playerx - 1; x < playerx + 2; x++) + { + for (y = playery - 1; y < playery + 2; y++) + { + + m = mitem[x][y]; + + if (m == 0) + { + + continue; + } + + if (nospell (spnum, m) == 0) + { + + ifblind (x, y); + cursors (); + lprc ('\n'); + lprintf (str, lastmonst); + hitm (x, y, dam); + nap (NAPTIME); + + } + else + { + + lasthx = x; + lasthy = y; + } + } + } +} + + + +/* +* dirsub(x,y) Routine to ask for direction, then modify playerx, +* playery for it +* int *x,*y; +* +* Function to ask for a direction and modify an x,y for that direction +* Enter with the coordinate destination (x,y). +* Returns index into diroffx[] (0-8). +*/ +int +dirsub (int *x, int *y) +{ + int i; + + lprcat ("\nIn What Direction? "); + + for (i = 0;;) + { + + switch (ttgetch ()) + { + case 'b': + i++; + case 'n': + i++; + case 'y': + i++; + case 'u': + i++; + case 'h': + i++; + case 'k': + i++; + case 'l': + i++; + case 'j': + i++; + goto out; +/* Added an ESC. -Gibbon */ + case '\33': + drawscreen(); + goto out; + }; + } + +out: + *x = playerx + diroffx[i]; + *y = playery + diroffy[i]; + + vxy (x, y); + + return i; +} + + + +/* +* dirpoly(spnum) Routine to ask for a direction and polymorph a monst +* int spnum; +* +* Subroutine to polymorph a monster and ask for the direction its in +* Enter with the spell number in spmun. +* Returns no value. +*/ +static void +dirpoly (int spnum) +{ + int x, y, m; + + /* bad args */ + if (spnum < 0 || spnum >= SPNUM) + { + + return; + } + + /* if he is confused, he can't aim his magic */ + if (isconfuse ()) + { + + return; + } + + dirsub (&x, &y); + + if (mitem[x][y] == 0) + { + + lprcat (" There wasn't anything there!"); + + return; + } + + ifblind (x, y); + + if (nospell (spnum, mitem[x][y])) + { + + lasthx = x; + lasthy = y; + + return; + } + + do + { + + m = rnd (MAXMONST + 7); + mitem[x][y] = m; + + } + while (monster[m].genocided); + + hitp[x][y] = monster[m].hitpoints; + + /* show the new monster */ + show1cell (x, y); +} + + + +/* +* annihilate() Routine to annihilate all monsters around player (playerx,playery) +* +* Gives player experience, but no dropped objects +* +*/ +void +annihilate (void) +{ + int i, j; + int k; + int *p; + + for (k = 0, i = playerx - 1; i <= playerx + 1; i++) + { + for (j = playery - 1; j <= playery + 1; j++) + { + + /* out of bounds */ + if (vxy (&i, &j)) + { + + continue; + } + + p = &mitem[i][j]; + + /* no monster here */ + if (*p == 0) + { + + continue; + } + + + if (*p < DEMONLORD + 2) + { + + k += monster[*p].experience; + + *p = know[i][j] &= ~KNOWHERE; + + } + else + { + + lprintf ("\nThe %s barely escapes being annihilated!", + monster[*p].name); + + /* lose half hit points */ + hitp[i][j] = (hitp[i][j] >> 1) + 1; + } + } + } + + if (k > 0) + { + lprcat ("\nYou hear loud screams of agony!"); + + raiseexperience (k); + } +} + + + +/* +* genmonst() Function to ask for monster and genocide from game +* +* This is done by setting a flag in the monster[] structure +*/ +static void +genmonst (void) +{ + int i, j; + + cursors (); + lprcat ("\nGenocide what monster? "); + + for (i = 0; !isalpha (i) && i != ' '; i = ttgetch ()) + { + ; + } + + lprint (i); + + /* search for the monster type */ + for (j = 0; j < MAXMONST; j++) + { + + /* have we found it? */ + if (monstnamelist[j] == i) + { + + /* genocided from game */ + monster[j].genocided = 1; + + lprintf (" There will be no more %s's", monster[j].name); + + /* now wipe out monsters on this level */ + newcavelevel (level); + draws (0, MAXX, 0, MAXY); + bot_linex (); + + return; + } + } + + lprcat (" You sense failure!"); +} diff --git a/src/spheres.c b/src/spheres.c new file mode 100644 index 0000000..3d79afc --- /dev/null +++ b/src/spheres.c @@ -0,0 +1,248 @@ +/* + newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation + rmsphere(x,y) Function to delete a sphere of annihilation from list + sphboom(x,y) Function to perform the effects of a sphere detonation + movsphere() Function to look for and move spheres of annihilation +*/ +#include + +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/help.h" +#include "includes/io.h" +#include "includes/monster.h" +#include "includes/scores.h" +#include "includes/spheres.h" +#include "includes/sysdep.h" + +static void sphboom (int x, int y); + + + +/* + * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation + * int x,y,dir,lifetime; + * + * Enter with the coordinates of the sphere in x,y + * the direction (0-8 diroffx format) in dir, and the lifespan of the + * sphere in lifetime (in turns) + * Returns the number of spheres currently in existence + */ +int +newsphere (int x, int y, int dir, int life) +{ + int m; + struct sphere *sp; + + if (((sp = (struct sphere *) malloc (sizeof (struct sphere)))) == 0) + return (cdesc[SPHCAST]); /* can't malloc, therefore failure */ + if (dir >= 9) + dir = 0; /* no movement if direction not found */ + if (level == 0) + vxy (&x, &y); /* don't go out of bounds */ + else + { + if (x < 1) + x = 1; + if (x >= MAXX - 1) + x = MAXX - 2; + if (y < 1) + y = 1; + if (y >= MAXY - 1) + y = MAXY - 2; + } + if ((m = mitem[x][y]) >= DEMONLORD + 4) /* demons dispel spheres */ + { + show1cell (x, y); /* show the demon (ha ha) */ + cursors (); + lprintf ("\nThe %s dispels the sphere!", monster[m].name); + rmsphere (x, y); /* remove any spheres that are here */ + return (cdesc[SPHCAST]); + } + if (m == DISENCHANTRESS) /* disenchantress cancels spheres */ + { + cursors (); + lprintf ("\nThe %s causes cancellation of the sphere!", + monster[m].name); + boom:sphboom (x, y); /* blow up stuff around sphere */ + rmsphere (x, y); /* remove any spheres that are here */ + return (cdesc[SPHCAST]); + } + if (cdesc[CANCELLATION]) /* cancellation cancels spheres */ + { + cursors (); + lprcat + ("\nAs the cancellation takes effect, you hear a great earth shaking blast!"); + goto boom; + } + if (item[x][y] == OANNIHILATION) /* collision of spheres detonates spheres */ + { + cursors (); + lprcat + ("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!"); + rmsphere (x, y); + goto boom; + } + if (playerx == x && playery == y) /* collision of sphere and player! */ + { + cursors (); + lprcat ("\nYou have been enveloped by the zone of nothingness!\n"); + rmsphere (x, y); /* remove any spheres that are here */ + nap (NAPTIME); + died (258); + } + item[x][y] = OANNIHILATION; + mitem[x][y] = 0; + know[x][y] = 1; + show1cell (x, y); /* show the new sphere */ + sp->x = x; + sp->y = y; + sp->lev = level; + sp->dir = dir; + sp->lifetime = life; + sp->p = 0; + if (spheres == 0) + spheres = sp; /* if first node in the sphere list */ + else /* add sphere to beginning of linked list */ + { + sp->p = spheres; + spheres = sp; + } + return (++cdesc[SPHCAST]); /* one more sphere in the world */ +} + + + + +/* + * rmsphere(x,y) Function to delete a sphere of annihilation from list + * int x,y; + * + * Enter with the coordinates of the sphere (on current level) + * Returns the number of spheres currently in existence + */ +int +rmsphere (int x, int y) +{ + struct sphere *sp, *sp2 = 0; + + for (sp = spheres; sp; sp2 = sp, sp = sp->p) + if (level == sp->lev) /* is sphere on this level? */ + if ((x == sp->x) && (y == sp->y)) /* locate sphere at this location */ + { + item[x][y] = mitem[x][y] = 0; + know[x][y] = 1; + show1cell (x, y); /* show the now missing sphere */ + --cdesc[SPHCAST]; + if (sp == spheres) + { + sp2 = sp; + spheres = sp->p; + free ((char *) sp2); + } + else + { + sp2->p = sp->p; + free ((char *) sp); + } + break; + } + + /* return number of spheres in the world */ + return cdesc[SPHCAST]; +} + + + +/* + * sphboom(x,y) Function to perform the effects of a sphere detonation + * int x,y; + * + * Enter with the coordinates of the blast, Returns no value + */ +static void +sphboom (int x, int y) +{ + int i, j; + + if (cdesc[HOLDMONST]) + cdesc[HOLDMONST] = 1; + if (cdesc[CANCELLATION]) + cdesc[CANCELLATION] = 1; + for (j = max (1, x - 2); j < min (x + 3, MAXX - 1); j++) + for (i = max (1, y - 2); i < min (y + 3, MAXY - 1); i++) + { + item[j][i] = mitem[j][i] = 0; + show1cell (j, i); + if (playerx == j && playery == i) + { + cursors (); + lprcat ("\nYou were too close to the sphere!"); + nap (NAPTIME); + died (283); /* player killed in explosion */ + } + } +} + + + +/* + * movsphere() Function to look for and move spheres of annihilation + * + * This function works on the sphere linked list, first duplicating the list + * (the act of moving changes the list), then processing each sphere in order + * to move it. They eat anything in their way, including stairs, volcanic + * shafts, potions, etc, except for upper level demons, who can dispel + * spheres. + * No value is returned. + */ +#define SPHMAX 20 /* maximum number of spheres movsphere can handle */ + +void +movsphere (void) +{ + int x, y, dir, len; + struct sphere *sp, *sp2; + struct sphere sph[SPHMAX]; + + /* first duplicate sphere list */ + for (sp = 0, x = 0, sp2 = spheres; sp2; sp2 = sp2->p) /* look through sphere list */ + if (sp2->lev == level) /* only if this level */ + { + sph[x] = *sp2; + sph[x++].p = 0; /* copy the struct */ + if (x > 1) + sph[x - 2].p = &sph[x - 1]; /* link pointers */ + } + if (x) + sp = sph; /* if any spheres, point to them */ + else + return; /* no spheres */ + + for (sp = sph; sp; sp = sp->p) /* look through sphere list */ + { + x = sp->x; + y = sp->y; + if (item[x][y] != OANNIHILATION) + continue; /* not really there */ + if (--(sp->lifetime) < 0) /* has sphere run out of gas? */ + { + rmsphere (x, y); /* delete sphere */ + continue; + } + switch (rnd ((int) max (7, cdesc[INTELLIGENCE] >> 1))) /* time to move the sphere */ + { + case 1: + case 2: /* change direction to a random one */ + sp->dir = rnd (8); + default: /* move in normal direction */ + dir = sp->dir; + len = sp->lifetime; + rmsphere (x, y); + newsphere (x + diroffx[dir], y + diroffy[dir], dir, len); + }; + } +} diff --git a/src/store.c b/src/store.c new file mode 100644 index 0000000..1204bae --- /dev/null +++ b/src/store.c @@ -0,0 +1,1234 @@ +/* store.c */ +/* +This module contains data and routines to handle buildings at the home level. +Routines: + +dnd_2hed +dnd_hed +dndstore The DND store main routine +handsfull To tell the player he can carry no more +out_of_stock To tell the player an item is out of stock +no_gold To tell the player he has no gold +dnditem Display DND store items +sch_head print the school header +oschool main school routine +obank Larn National Bank +obank2 5th level branch of the bank +banktitle bank header +ointerest accrue interest to bank account +obanksub bank main subroutine +otradhead trading post header +otradepost trading post main function +cnsitm +olrs larn revenue service function +*/ + +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/display.h" +#include "includes/global.h" +#include "includes/inventory.h" +#include "includes/io.h" +#include "includes/main.h" +#include "includes/object.h" +#include "includes/scores.h" +#include "includes/store.h" +#include "includes/sysdep.h" +#include + +static void dnd_2hed (void); +static void dnd_hed (void); +static void handsfull (void); +static void outofstock (void); +static void nogold (void); +static void dnditem (int i); +static void sch_hed (void); +static void banktitle (char *); +static void obanksub (void); +static void otradhead (void); +static void otradiven (void); +static void cleartradiven (int); +static void cnsitm (void); + +static int dndcount = 0, dnditm = 0; + +/* new function for displaying gold in inventory inside trading posts. + * part of feature request by hymie0. ~Gibbon */ +static int +amtgoldtrad(void) +{ + lprintf ("You have"); + attron(COLOR_PAIR(3)); + lprintf(" %-6d ",(int) cdesc[GOLD]); + attroff(COLOR_PAIR(3)); + lprintf("gold pieces."); + return(0); +} +/*Fix for bug #23 ~Gibbon*/ +static int +lrs_welcome_text(void) +{ + screen_clear(); + resetscroll(); + lprintf("Welcome to the Larn Revenue Service\n"); + return(0); +} +/* number of items in the dnd inventory table */ +#define MAXITM 83 + +/* this is the data for the stuff in the dnd store */ +struct _itm dnd_item[90] = { + /*cost iven name iven arg how + gp iven[] ivenarg[] many */ + + {2, OLEATHER, 0, 3}, + {10, OSTUDLEATHER, 0, 2}, + {40, ORING, 0, 2}, + {85, OCHAIN, 0, 2}, + {220, OSPLINT, 0, 1}, + {400, OPLATE, 0, 1}, + {900, OPLATEARMOR, 0, 1}, + {2600, OSSPLATE, 0, 1}, + {150, OSHIELD, 0, 1}, + + /*cost memory iven name iven arg how + gp pointer iven[] ivenarg[] many */ + + {2, ODAGGER, 0, 3}, + {20, OSPEAR, 0, 3}, + {150, OBATTLEAXE, 0, 2}, + {450, OLONGSWORD, 0, 2}, + {1000, O2SWORD, 0, 2}, + {5000, OSWORD, 0, 1}, + + /* lets make it twice as expensive. ~Gibbon */ + {30000, OGREATSWORD, 0, 1}, + {6000, OSWORDofSLASHING, 0, 0}, + {10000, OHAMMER, 0, 0}, + + /*cost memory iven name iven arg how + gp pointer iven[] ivenarg[] many */ + + {150, OPROTRING, 1, 1}, + {85, OSTRRING, 1, 1}, + {120, ODEXRING, 1, 1}, + {120, OCLEVERRING, 1, 1}, + {180, OENERGYRING, 0, 1}, + {125, ODAMRING, 0, 1}, + {220, OREGENRING, 0, 1}, + {1000, ORINGOFEXTRA, 0, 1}, + + {280, OBELT, 0, 1}, + + {400, OAMULET, 0, 1}, + + {6500, OORBOFDRAGON, 0, 0}, + {5500, OSPIRITSCARAB, 0, 0}, + {5000, OCUBEofUNDEAD, 0, 0}, + {6000, ONOTHEFT, 0, 0}, + + {590, OCHEST, 6, 1}, + {200, OBOOK, 8, 1}, + {10, OCOOKIE, 0, 3}, + + /*cost memory iven name iven arg how + gp pointer iven[] ivenarg[] many */ + + {20, OPOTION, 0, 6}, + {90, OPOTION, 1, 5}, + {520, OPOTION, 2, 1}, + {100, OPOTION, 3, 2}, + {50, OPOTION, 4, 2}, + {150, OPOTION, 5, 2}, + {70, OPOTION, 6, 1}, + {30, OPOTION, 7, 7}, + {200, OPOTION, 8, 1}, + {50, OPOTION, 9, 1}, + {80, OPOTION, 10, 1}, + + /*cost memory iven name iven arg how + gp pointer iven[] ivenarg[] many */ + + {30, OPOTION, 11, 3}, + {20, OPOTION, 12, 5}, + {40, OPOTION, 13, 3}, + {35, OPOTION, 14, 2}, + {520, OPOTION, 15, 1}, + {90, OPOTION, 16, 2}, + {200, OPOTION, 17, 2}, + {220, OPOTION, 18, 4}, + {80, OPOTION, 19, 6}, + {370, OPOTION, 20, 3}, + {50, OPOTION, 22, 1}, + {150, OPOTION, 23, 3}, + + /*cost memory iven name iven arg how + gp pointer iven[] ivenarg[] many */ + + {100, OSCROLL, 0, 2}, + {125, OSCROLL, 1, 2}, + {60, OSCROLL, 2, 4}, + {10, OSCROLL, 3, 4}, + {100, OSCROLL, 4, 3}, + {200, OSCROLL, 5, 2}, + {110, OSCROLL, 6, 1}, + {500, OSCROLL, 7, 2}, + {200, OSCROLL, 8, 2}, + {250, OSCROLL, 9, 4}, + {20, OSCROLL, 10, 5}, + {30, OSCROLL, 11, 3}, + + /*cost memory iven name iven arg how + gp pointer iven[] ivenarg[] many */ + + {340, OSCROLL, 12, 1}, + {340, OSCROLL, 13, 1}, + {300, OSCROLL, 14, 2}, + {400, OSCROLL, 15, 2}, + {500, OSCROLL, 16, 2}, + {1000, OSCROLL, 17, 1}, + {500, OSCROLL, 18, 1}, + {340, OSCROLL, 19, 2}, + {220, OSCROLL, 20, 3}, + {3900, OSCROLL, 21, 0}, + {610, OSCROLL, 22, 1}, + {3000, OSCROLL, 23, 0} +}; + +/* +for the college of larn +*/ +int course[26]; /* the list of courses taken */ +static char coursetime[] = { 10, 15, 10, 20, 10, 10, 10, 5 }; + + +/* +function for the dnd store +*/ +static void +dnd_2hed (void) +{ + lprcat + ("Welcome to the Larn Thrift Shoppe. We stock many items explorers find useful\n"); + lprcat + (" in their adventures. Feel free to browse to your hearts content.\n"); + lprcat ("Also be advised, if you break 'em, you pay for 'em."); +} + + +static void +dnd_hed (void) +{ + int i; + + for (i = dnditm; i < 26 + dnditm; i++) + { + dnditem (i); + } +} + + +void +dndstore (void) +{ + int i; + + dnditm = 0; + screen_clear(); + dnd_2hed (); + + if (outstanding_taxes > 0) + { + lprcat + ("\n\nThe Larn Revenue Service has ordered us to not do business with tax evaders.\n"); + lprintf + ("They have also told us that you owe %d gp in back taxes, and as we must\n", + (int) outstanding_taxes); + lprcat + ("comply with the law, we cannot serve you at this time. Soo Sorry.\n"); + cursors (); + lprcat ("\nPress "); + lstandout ("escape"); + lprcat (" to leave: "); + lflush (); + + i = 0; + while (i != '\33') + { + i = ttgetch (); + } + + drawscreen (); + return; + } + + dnd_hed (); + + for (;;) + { + + cursor (1, 19); + lprcat ("You have "); + attron(COLOR_PAIR(3)); + lprintf ("%ld ",cdesc[GOLD]); + attroff(COLOR_PAIR(3)); + lprintf("gold pieces"); + + cltoeoln (); + cl_dn (1, 20); + + lprcat ("\nEnter your transaction ["); + lstandout ("space"); + lprcat (" for more, "); + lstandout ("escape"); + lprcat (" to leave]? "); + + i = 0; + while ((i < 'a' || i > 'z') && (i != ' ') && (i != '\33') && (i != 12)) + { + i = ttgetch (); + } + + if (i == 12) + { + screen_clear(); + dnd_2hed (); + dnd_hed (); + } + else if (i == '\33') + { + drawscreen (); + return; + } + else if (i == ' ') + { + + cl_dn (1, 4); + + if ((dnditm += 26) >= MAXITM) + { + + dnditm = 0; + } + + dnd_hed (); + + } + else + { /* buy something */ + lprc ((char) i); /* echo the byte */ + i += dnditm - 'a'; + if (i >= MAXITM) + outofstock (); + else if (dnd_item[i].qty <= 0) + outofstock (); + else if (pocketfull ()) + handsfull (); + else if (cdesc[GOLD] < (dnd_item[i].price) * 10L) + nogold (); + else + { + + if (dnd_item[i].obj == OPOTION) + { + potionname[dnd_item[i].arg][0] = ' '; + } + else if (dnd_item[i].obj == OSCROLL) + { + scrollname[dnd_item[i].arg][0] = ' '; + } + cdesc[GOLD] -= dnd_item[i].price * 10; + dnd_item[i].qty--; + take (dnd_item[i].obj, dnd_item[i].arg); + if (dnd_item[i].qty == 0) + dnditem (i); + lflush (); + nap (NAPTIME); + } + } + + } +} + + + + +/* +function for the players hands are full +*/ +static void +handsfull (void) +{ + + lprcat ("\nYou can't carry anything more!"); + lflush (); + nap (NAPTIME); +} + + +static void +outofstock (void) +{ + + lprcat ("\nSorry, but we are out of that item."); + lflush (); + nap (NAPTIME); +} + +static void +nogold (void) +{ + lprcat ("\nYou don't have enough gold to pay for that!"); + lflush (); + nap (2200); +} + + +/* +dnditem(index) + +to print the item list; used in dndstore() enter with the index into itm +*/ +static void +dnditem (int i) +{ + + int j, k; + int price; + + if (i >= MAXITM) + { + + return; + } + + cursor ((j = (i & 1) * 40 + 1), (k = ((i % 26) >> 1) + 5)); + + if (dnd_item[i].qty == 0) + { + attron(COLOR_PAIR(2)); + lprintf ("%39s", ""); + attroff(COLOR_PAIR(2)); + + return; + } +attron(COLOR_PAIR(4)); + lprintf ("%c) ", (i % 26) + 'a'); +attroff(COLOR_PAIR(4)); + if (dnd_item[i].obj == OPOTION) + { + lprcat ("potion of "); + lprintf ("%s", &potionname[dnd_item[i].arg][1]); + } + else if (dnd_item[i].obj == OSCROLL) + { + lprcat ("scroll of "); + lprintf ("%s", &scrollname[dnd_item[i].arg][1]); + } + else + { + lprintf ("%s", objectname[dnd_item[i].obj]); + } + + + cursor (j + 31, k); + + price = ((int) dnd_item[i].price) * 10L; +attron(COLOR_PAIR(3)); + lprintf ("%6ld", price); +attroff(COLOR_PAIR(3)); +} + + + +/* +function to display the header info for the school +*/ +static void +sch_hed (void) +{ + + screen_clear(); + lprcat + ("The College of Larn offers the exciting opportunity of higher education to\n"); + lprcat + ("all inhabitants of the caves. Here is a list of the class schedule:\n\n\n"); + lprcat ("\t\t Course Name \t Time Needed\n\n"); + + if (course[0] == 0) + lprcat ("\t\ta) Fighters Training I 10 mobuls"); /*line 7 of crt */ + lprc ('\n'); + if (course[1] == 0) + lprcat ("\t\tb) Fighters Training II 15 mobuls"); + lprc ('\n'); + if (course[2] == 0) + lprcat ("\t\tc) Introduction to Wizardry 10 mobuls"); + lprc ('\n'); + if (course[3] == 0) + lprcat ("\t\td) Applied Wizardry 20 mobuls"); + lprc ('\n'); + if (course[4] == 0) + lprcat ("\t\te) Behavioral Psychology 10 mobuls"); + lprc ('\n'); + if (course[5] == 0) + lprcat ("\t\tf) Faith for Today 10 mobuls"); + lprc ('\n'); + if (course[6] == 0) + lprcat ("\t\tg) Contemporary Dance 10 mobuls"); + lprc ('\n'); + if (course[7] == 0) + lprcat ("\t\th) History of Larn 5 mobuls"); + + lprcat ("\n\n\t\tAll courses cost 250 gold pieces."); + cursor (1, 19); + lprcat ("You are presently carrying "); +} + + + +void +oschool (void) +{ + int i; + int time_used; + + sch_hed (); + for (;;) + { + cursor (1, 19); + attron(COLOR_PAIR(3)); + lprintf ("%d ",(int) cdesc[GOLD]); + attroff(COLOR_PAIR(3)); + lprintf("gold pieces."); + cursors (); + lprcat ("\nWhat is your choice ["); + lstandout ("escape"); + lprcat (" to leave] ? "); + yrepcount = 0; + i = 0; + while ((i < 'a' || i > 'h') && (i != '\33') && (i != 12)) + i = ttgetch (); + if (i == 12) + { + sch_hed (); + continue; + } + else if (i == '\33') + { + drawscreen (); + return; + } + lprc ((char) i); + if (cdesc[GOLD] < 250) + nogold (); + else if (course[i - 'a']) + { + lprcat ("\nSorry, but that class is filled."); + nap (NAPTIME); + } + else if (i <= 'h') + { + cdesc[GOLD] -= 250; + time_used = 0; + switch (i) + { + case 'a': + cdesc[STRENGTH] += 2; + cdesc[CONSTITUTION]++; + lprcat ("\nYou feel stronger!"); + cl_line (16, 7); + break; + + case 'b': + if (course[0] == 0) + { + lprcat + ("\nSorry, but this class has a prerequisite of Fighters Training I"); + cdesc[GOLD] += 250; + time_used = -10000; + break; + } + lprcat ("\nYou feel much stronger!"); + cl_line (16, 8); + cdesc[STRENGTH] += 2; + cdesc[CONSTITUTION] += 2; + break; + + case 'c': + cdesc[INTELLIGENCE] += 2; + lprcat ("\nThe task before you now seems more attainable!"); + cl_line (16, 9); + break; + + case 'd': + if (course[2] == 0) + { + lprcat + ("\nSorry, but this class has a prerequisite of Introduction to Wizardry"); + cdesc[GOLD] += 250; + time_used = -10000; + break; + } + lprcat ("\nThe task before you now seems very attainable!"); + cl_line (16, 10); + cdesc[INTELLIGENCE] += 2; + break; + + case 'e': + cdesc[CHARISMA] += 3; + lprcat ("\nYou now feel like a born leader!"); + cl_line (16, 11); + break; + + case 'f': + cdesc[WISDOM] += 2; + lprcat + ("\nYou now feel more confident that you can find the potion in time!"); + cl_line (16, 12); + break; + + case 'g': + cdesc[DEXTERITY] += 3; + lprcat ("\nYou feel like dancing!"); + cl_line (16, 13); + break; + + case 'h': + cdesc[INTELLIGENCE]++; + lprcat + ("\nYour instructor told you that the Eye of Larn is rumored to be guarded\n"); + lprcat + ("by a platinum dragon who possesses psionic abilities. "); + cl_line (16, 14); + break; + } + time_used += coursetime[i - 'a'] * 100; + if (time_used > 0) + { + gtime += time_used; + course[i - 'a']++; /* remember that he has taken that course */ + cdesc[HP] = cdesc[HPMAX]; + cdesc[SPELLS] = cdesc[SPELLMAX]; /* he regenerated */ + + if (cdesc[BLINDCOUNT]) + cdesc[BLINDCOUNT] = 1; /* cure blindness too! */ + if (cdesc[CONFUSE]) + cdesc[CONFUSE] = 1; /* end confusion */ + adjtimel ((int) time_used); /* adjust parameters for time change */ + } + nap (NAPTIME); + } + } +} + +/* +* for the first national bank of Larn +*/ +int lasttime = 0; /* last time he was in bank */ + +void +obank (void) +{ + + banktitle (" Welcome to the First National Bank of Larn."); +} + + +void +obank2 (void) +{ + + banktitle ("Welcome to the 5th level branch office of " + "the First National Bank of Larn."); + + /* because we state the level in the title, clear the '?' in the + level display at the bottom, if the user teleported. + */ + cdesc[TELEFLAG] = 0; +} + + +static void +banktitle (char *str) +{ + screen_clear(); + lprcat (str); + if (outstanding_taxes > 0) + { + int i; + lprcat + ("\n\nThe Larn Revenue Service has ordered that your account be frozen until all\n"); + lprintf + ("levied taxes have been paid. They have also told us that you owe %d gp in\n", + (int) outstanding_taxes); + lprcat + ("taxes, and we must comply with them. We cannot serve you at this time. Sorry.\n"); + lprcat ("We suggest you go to the LRS office and pay your taxes.\n"); + cursors (); + lprcat ("\nPress "); + lstandout ("escape"); + lprcat (" to leave: "); + lflush (); + i = 0; + while (i != '\33') + i = ttgetch (); + drawscreen (); + return; + } + obanksub (); + drawscreen (); +} + + + +/* +* function to put interest on your bank account +*/ +void +ointerest (void) +{ + int i; + + if (cdesc[BANKACCOUNT] < 0) + cdesc[BANKACCOUNT] = 0; + else if ((cdesc[BANKACCOUNT] > 0) && (cdesc[BANKACCOUNT] < 500000)) + { + i = (gtime - lasttime) / 100; /* # mobuls elapsed */ + while ((i-- > 0) && (cdesc[BANKACCOUNT] < 500000)) + cdesc[BANKACCOUNT] += cdesc[BANKACCOUNT] / 250; + if (cdesc[BANKACCOUNT] > 500000) + cdesc[BANKACCOUNT] = 500000; /* interest limit */ + } + lasttime = (gtime / 100) * 100; +} + +static void +obanksub (void) +{ + /* the reference to screen location for each gem */ + int gemorder[26]; + /* the appraisal of the gems */ + int gemvalue[26]; + int amt; + int i, k, gems_sold = 0; + + lprcat ("\n\n\tGemstone\t Appraisal\t\tGemstone\t Appraisal"); + + /* credit any needed interest */ + ointerest (); + + for (k = i = 0; i < 26; i++) + switch (iven[i]) + { + case OLARNEYE: + case ODIAMOND: + case OEMERALD: + case ORUBY: + case OSAPPHIRE: + + if (iven[i] == OLARNEYE) + { + gemvalue[i] = 250000 - ((gtime * 7) / 100) * 100; + if (gemvalue[i] < 50000) + gemvalue[i] = 50000; + } + else + gemvalue[i] = (255 & ivenarg[i]) * 100; + gemorder[i] = k; + cursor ((k % 2) * 40 + 1, (k >> 1) + 4); + lprintf ("%c) %s", i + 'a', objectname[iven[i]]); + cursor ((k % 2) * 40 + 33, (k >> 1) + 4); + lprintf ("%5d", (int) gemvalue[i]); + k++; + break; + + default: /* make sure player can't sell non-existant gems */ + gemvalue[i] = 0; + gemorder[i] = 0; + }; + +/* Cleaning up the awful UI here for the text. -Gibbon */ + + cursor(1,13); + lprintf("Account Summary:"); + cursor(1,15); + lprintf("Gold in Bank Account"); + cursor (1, 16); + attron(COLOR_PAIR(3)); + lprintf("%8d",(long)cdesc[BANKACCOUNT]); + attroff(COLOR_PAIR(3)); + cursor(1,18); + lprintf("Gold in inventory"); + cursor (1, 19); + attron(COLOR_PAIR(3)); + lprintf("%8d",(long)cdesc[GOLD]); + attroff(COLOR_PAIR(3)); + if (cdesc[BANKACCOUNT] + cdesc[GOLD] >= 500000) + lprcat + ("\nNote: Larndom law states that only deposits under 500,000gp can earn interest."); + for (;;) + { + cl_dn (1, 20); + lprcat ("\nYour wish? [("); + lstandout ("d"); + lprcat (") deposit, ("); + lstandout ("w"); + lprcat (") withdraw, ("); + lstandout ("s"); + lprcat (") sell a stone, or "); + lstandout ("escape"); + lprcat ("] "); + yrepcount = 0; + i = 0; + while (i != 'd' && i != 'w' && i != 's' && i != '\33') + i = ttgetch (); + switch (i) + { + case 'd': + lprcat ("deposit\n"); + cltoeoln (); + lprcat ("How much? "); + amt = readnum ((int) cdesc[GOLD]); + if (amt > cdesc[GOLD]) + { + lprcat ("You don't have that much."); + nap (NAPTIME); + } +/* Added if statement for catching 0 value to deposit. -Gibbon */ + if (amt == 0) + { + lprcat ("You cannot deposit nothing"); + nap (NAPTIME); + } + else + { + cdesc[GOLD] -= amt; + cdesc[BANKACCOUNT] += amt; + } + break; + + case 'w': + lprcat ("withdraw\nHow much? "); + amt = readnum ((int) cdesc[BANKACCOUNT]); + + if (amt > cdesc[BANKACCOUNT]) + { + lprcat ("\nYou don't have that much in the bank!"); + nap (NAPTIME); + } + else + { + cdesc[GOLD] += amt; + cdesc[BANKACCOUNT] -= amt; + } + break; + + case 's': + lprcat ("\nWhich stone would you like to sell? "); + i = 0; + while ((i < 'a' || i > 'z') && i != '*' && i != '\33') + i = ttgetch (); + if (i == '*') + { + for (i = 0; i < 26; i++) + { + if (gemvalue[i]) + { + gems_sold = TRUE; + cdesc[GOLD] += gemvalue[i]; + iven[i] = 0; + gemvalue[i] = 0; + k = gemorder[i]; + cursor ((k % 2) * 40 + 1, (k >> 1) + 4); + lprintf ("%39s", ""); + } + } + if (!gems_sold) + { + lprcat ("\nYou have no gems to sell!"); + nap (NAPTIME); + } + } + else if (i != '\33') + { + if (gemvalue[i = i - 'a'] == 0) + { + lprintf ("\nItem %c is not a gemstone!", i + 'a'); + nap (NAPTIME); + break; + } + cdesc[GOLD] += gemvalue[i]; + iven[i] = 0; + gemvalue[i] = 0; + k = gemorder[i]; + cursor ((k % 2) * 40 + 1, (k >> 1) + 4); + attron(COLOR_PAIR(2)); + lprintf ("%39s", ""); + attroff(COLOR_PAIR(2)); + } + break; + + case '\33': + return; + }; + /*Fix for #38 -Gibbon*/ + cursor(1,16); + attron(COLOR_PAIR(3)); + lprintf("%8d",(long)cdesc[BANKACCOUNT]); + attroff(COLOR_PAIR(3)); + cursor(1,19); + attron(COLOR_PAIR(3)); + lprintf("%8d",(long)cdesc[GOLD]); + attroff(COLOR_PAIR(3)); + } +} + +/* +function for the trading post +*/ +static void +otradhead (void) +{ + screen_clear(); + lprcat + ("Welcome to the Larn Trading Post. We buy items that explorers no longer find\n"); + lprcat + ("useful. Since the condition of the items you bring in is not certain,\n"); + lprcat + ("and we incur great expense in reconditioning the items, we usually pay\n"); + lprcat + ("only 20% of their value were they to be new. If the items are badly\n"); + lprcat ("damaged, we will pay only 10% of their new value.\n\n"); + + lprcat ("Here are the items we would be willing to buy from you:\n"); +} + + +static int tradorder[26]; /* screen locations for trading post inventory */ + +static void +otradiven (void) +{ + int i, j; + + /* Print user's inventory like bank */ + for (j = i = 0; i < 26; i++) + if (iven[i]) + { + cursor ((j % 2) * 40 + 1, (j >> 1) + 8); + tradorder[i] = 0; /* init position on screen to zero */ + switch (iven[i]) + { + case OPOTION: + if (potionname[ivenarg[i]][0] != 0) + { + tradorder[i] = j++; /* will display only if identified */ + lprintf ("%c) %s", i + 'a', objectname[iven[i]]); + lprintf (" of%s", potionname[ivenarg[i]]); + } + break; + case OSCROLL: + if (scrollname[ivenarg[i]][0] != 0) + { + tradorder[i] = j++; /* will display only if identified */ + lprintf ("%c) %s", i + 'a', objectname[iven[i]]); + lprintf (" of%s", scrollname[ivenarg[i]]); + } + break; + case OLARNEYE: + case OBOOK: + case OSPIRITSCARAB: + case ODIAMOND: + case ORUBY: + case OEMERALD: + case OCHEST: + case OSAPPHIRE: + case OCUBEofUNDEAD: + case OCOOKIE: + case ONOTHEFT: + tradorder[i] = j++; /* put on screen */ + attron(COLOR_PAIR(4)); + lprintf ("%c) ",i + 'a'); + attroff(COLOR_PAIR(4)); + lprintf("%s",objectname[iven[i]]); + break; + default: + tradorder[i] = j++; /* put on screen */ + attron(COLOR_PAIR(4)); + lprintf ("%c) ",i + 'a'); + attroff(COLOR_PAIR(4)); + lprintf("%s",objectname[iven[i]]); + if (ivenarg[i] > 0) + { + lprintf (" +%d", (int) ivenarg[i]); + } + else if (ivenarg[i] < 0) { + lprintf (" %d", (int) ivenarg[i]); + } + break; + } + } + else + tradorder[i] = 0; /* make sure order array is clear */ +} + + + +static void +cleartradiven (int i) +{ + int j; + + j = tradorder[i]; + + cursor ((j % 2) * 40 + 1, (j >> 1) + 8); +attron(COLOR_PAIR(2)); + lprintf ("%39s", ""); + attroff(COLOR_PAIR(2)); + + tradorder[i] = 0; +} + + + +void +otradepost (void) +{ + int i, j, isub, izarg, found; + int value; + + dnditm = dndcount = 0; + otradhead (); + otradiven (); + + for (;;) + { + cursor (1, 23); + lprcat ("\nWhat item do you want to sell to us ["); + lstandout ("escape"); + lprcat ("] ? "); + + /* display gold in inventory ~Gibbon */ + cursor(1,22); + amtgoldtrad(); + + i = 0; + while ((i > 'z' || i < 'a') && i != 12 && i != '\33') + i = ttgetch (); + if (i == '\33') + { + + recalc (); + drawscreen (); + return; + } + for (;;) /* inner loop for simpler control */ + { + if (i == 12) + { + screen_clear(); + otradhead (); + otradiven (); + break; /* leave inner while */ + } + + isub = i - 'a'; + if (iven[isub] == 0) + { + lprintf ("\nYou don't have item %c!", isub + 'a'); + nap (NAPTIME); + break; /* leave inner while */ + } + if (iven[isub] == OSCROLL) + if (scrollname[ivenarg[isub]][0] == 0) + { + cnsitm (); + break; /* leave inner while */ + } + if (iven[isub] == OPOTION) + if (potionname[ivenarg[isub]][0] == 0) + { + cnsitm (); + break; /* leave inner while */ + } + if (iven[isub] == ODIAMOND || + iven[isub] == ORUBY || + iven[isub] == OEMERALD || iven[isub] == OSAPPHIRE) + value = 20L * (ivenarg[isub] & 255); + else if (iven[isub] == OLARNEYE) + { + value = 50000 - (((gtime * 7) / 100) * 20); + if (value < 10000) + value = 10000; + } + else + { + /* find object in dnd_item[] list for price info */ + found = MAXITM; + for (j = 0; j < MAXITM; j++) + if (dnd_item[j].obj == iven[isub]) + { + found = j; + break; /* leave for loop */ + } + if (found == MAXITM) + { + lprcat + ("\nSo sorry, but we are not authorized to accept that item."); + nap (NAPTIME); + break; /* leave inner while */ + } + if (iven[isub] == OSCROLL || iven[isub] == OPOTION) + value = 2 * (int) dnd_item[j + ivenarg[isub]].price; + else + { + izarg = ivenarg[isub]; + value = dnd_item[j].price; + /* appreciate if a +n object */ + if (izarg >= 0) + value *= 2; + while ((izarg-- > 0) + && ((value = 14 * (67 + value) / 10) < 500000)); + } + } + /* we have now found the value of the item, and dealt with any error + cases. Print the object's value, let the user sell it. + */ + /*Fix for bug #40, overwriting some text at the same position to cancel out + the previous text. -Gibbon*/ + cursor(1,23); + lprintf + ("\nItem (%c) is worth %d gold pieces to us. Do you want to sell it? ", + i, (int) value); + yrepcount = 0; + if (getyn () == 'y') + { + lprcat ("yes\n"); + cdesc[GOLD] += value; + if (cdesc[WEAR] == isub) + cdesc[WEAR] = -1; + if (cdesc[WIELD] == isub) + cdesc[WIELD] = -1; + if (cdesc[SHIELD] == isub) + cdesc[SHIELD] = -1; + adjustcvalues (iven[isub], ivenarg[isub]); + iven[isub] = 0; + cleartradiven (isub); + + /* clear and display functions again so gold is re-calculated + * part of feature request from hymie0. ~Gibbon */ + screen_clear(); + otradhead (); + otradiven (); + } + else + { + + /* refresh screen when saying no ~Gibbon */ + screen_clear(); + otradhead (); + otradiven (); + } + break; /* exit inner while */ + } /* end of inner while */ + } /* end of outer while */ +} /* end of routine */ + + + + +static void +cnsitm (void) +{ + lprcat ("\nSorry, we can't accept unidentified objects."); + + nap (2000); +} + + +/* +* for the Larn Revenue Service +* +* I have cleaned this up and extended it with reusable functions +* with exception of lrs_welcome_text() which simply clears the +* screen and redraws. ~Gibbon. +*/ +void +olrs (void) +{ + int i, first; + int amt; + + lrs_welcome_text(); + /* disable signals */ + first = 1; + for (;;) + { + if (first) + { + first = 0; + /* this is gonna go at somepoint. ~Gibbon*/ + goto nxt; + } + setscroll(); + cursor(1,21); + lprcat ("\n\nYour wish? [("); + lstandout ("p"); + lprcat (") pay taxes, or "); + lstandout ("escape"); + lprcat ("] "); + yrepcount = 0; + i = 0; + + while (i != 'p' && i != '\33') + { + i = ttgetch (); + } + switch (i) + { + case 'p': + lprcat ("pay taxes\nHow much? "); + amt = readnum ((int) cdesc[GOLD]); + + if (amt > cdesc[GOLD]) + { + lprcat (" You don't have that much.\n"); + } + else + { + /*Fix for bug #23 ~Gibbon*/ + cdesc[GOLD] -= paytaxes ((int) amt); + lrs_welcome_text(); + } + break; + + case '\33': + setscroll(); + drawscreen(); + return; + }; + + nxt: + cursor (1, 6); + if (outstanding_taxes > 0) + { + lprintf ("You presently owe %d gp in taxes. ", + (int) outstanding_taxes); + } + else + { + lprcat ("You do not owe us any taxes. "); + } + + cursor (1, 19); + if (cdesc[GOLD] > 0) + { + amtgoldtrad(); + } + else + { + lprcat ("You have no gold pieces. "); + } + } +} diff --git a/src/sysdep.c b/src/sysdep.c new file mode 100644 index 0000000..31e90f1 --- /dev/null +++ b/src/sysdep.c @@ -0,0 +1,25 @@ +#include "includes/larnfunc.h" +#include "includes/io.h" +#include "includes/sysdep.h" + +#if defined WINDOWS +#include +#endif + +#if defined NIX +#include +#endif + +void nap(int milliseconds) +{ +#if defined WINDOWS + Sleep(milliseconds); +#endif + +#if defined NIX + struct timespec tc; + tc.tv_sec = milliseconds / 1000; + tc.tv_nsec = (milliseconds % 1000) * 1000000; + nanosleep(&tc, NULL); +#endif +} diff --git a/src/tgoto.c b/src/tgoto.c new file mode 100644 index 0000000..5aa90e4 --- /dev/null +++ b/src/tgoto.c @@ -0,0 +1,264 @@ +/************************************************************************ + * * + * Copyright (c) 1982, Fred Fish * + * All Rights Reserved * + * * + * This software and/or documentation is released for public * + * distribution for personal, non-commercial use only. * + * Limited rights to use, modify, and redistribute are hereby * + * granted for non-commercial purposes, provided that all * + * copyright notices remain intact and all changes are clearly * + * documented. The author makes no warranty of any kind with * + * respect to this product and explicitly disclaims any implied * + * warranties of merchantability or fitness for any particular * + * purpose. * + * * + ************************************************************************ + */ + +/* + * LIBRARY FUNCTION + * + * tgoto expand cursor addressing string from cm capability + * + * KEY WORDS + * + * termcap + * + * SYNOPSIS + * + * char *tgoto(cm,destcol,destline) + * char *cm; + * int destcol; + * int destline; + * + * DESCRIPTION + * + * Returns cursor addressing string, decoded from the cm + * capability string, to move cursor to column destcol on + * line destline. + * + * The following sequences uses one input argument, either + * line or column, and place the appropriate substitution + * in the output string: + * + * %d substitute decimal value (in ASCII) + * %2 like %d but forces field width to 2 + * %3 like %d but forces field width to 3 + * %. like %c + * %+x like %c but adds ASCII value of x + * + * The following sequences cause processing modifications + * but do not "use up" one of the arguments. If they + * act on an argument they act on the next one to + * be converted. + * + * %>xy if next value to be converted is + * greater than value of ASCII char x + * then add value of ASCII char y. + * %r reverse substitution of line + * and column (line is substituted + * first by default). + * %i causes input values destcol and + * destline to be incremented. + * %% gives single % character in output. + * + * BUGS + * + * Does not implement some of the more arcane sequences for + * radically weird terminals (specifically %n, %B, & %D). + * If you have one of these you deserve whatever happens. + * + */ + +/* + * Miscellaneous stuff + */ + +#include +#include +#include "includes/tgoto.h" + +#define MAXARGS 2 + +static void process (void); + + +static const char *in; /* Internal copy of input string pointer */ +static char *out; /* Pointer to output array */ +static int args[MAXARGS]; /* Maximum number of args to convert */ +static int pcount; /* Count of args processed */ +static char output[64]; /* Converted string */ + +/* + * PSEUDO CODE + * + * Begin tgoto + * If no string to process then + * Return pointer to error string. + * Else + * Initialize pointer to input string. + * Initialize pointer to result string. + * First arg is line number by default. + * Second arg is col number by default. + * No arguments processed yet. + * While there is another character to process + * If character is a not a % character then + * Simply copy to output. + * Else + * Process the control sequence. + * End if + * End while + * TERMINATE STRING! (rde) + * Return pointer to static output string. + * End if + * End tgoto + * + */ + +const char * +atgoto (const char *cm, int destcol, int destline) +{ + if (cm == NULL) + { + return ("OOPS"); + } + else + { + in = cm; + out = output; + args[0] = destline; + args[1] = destcol; + pcount = 0; + while (*in != '\0') + { + if (*in != '%') + { + *out++ = *in++; + } + else + { + process (); + } + } + *out = '\0'; /* rde 18-DEC-86: don't assume out was all zeros */ + return (output); + } +} + +/* + * INTERNAL FUNCTION + * + * process process the conversion/command sequence + * + * SYNOPSIS + * + * static process() + * + * DESCRIPTION + * + * Processes the sequence beginning with the % character. + * Directly manipulates the input string pointer, the + * output string pointer, and the arguments. Leaves + * the input string pointer pointing to the next character + * to be processed, and the output string pointer pointing + * to the next output location. If conversion of + * one of the numeric arguments occurs, then the pcount + * is incremented. + * + */ + +/* + * PSEUDO CODE + * + * Begin process + * Skip over the % character. + * Switch on next character after % + * Case 'd': + * Process %d type conversion (variable width). + * Reinitialize output pointer. + * Break; + * Case '2': + * Process %d type conversion (width 2). + * Reinitialize output pointer. + * Break; + * Case '3': + * Process %d type conversion (width 3). + * Reinitialize output pointer. + * Break; + * Case '.' + * Process %c type conversion. + * Break; + * Case '+': + * Process %c type conversion with offset. + * Break; + * Case '>': + * Process argument modification. + * Break; + * Case 'r': + * Process argument reversal. + * Break; + * Case 'i': + * Increment argument values. + * Break; + * Case '%': + * Copy to output, incrementing pointers. + * Break; + * End switch + * End process + * + */ + +static void +process (void) +{ + int temp; + + in++; + switch (*in++) + { + case 'd': + sprintf (out, "%d", args[pcount++]); + out = &output[strlen (output)]; + break; + case '2': + sprintf (out, "%02d", args[pcount++]); + out += 2; +/* + out = &output[strlen(output)]; +*/ + break; + case '3': + sprintf (out, "%03d", args[pcount++]); + out = &output[strlen (output)]; + break; + case '.': + *out++ = (char) args[pcount++]; + break; + case '+': + *out++ = (char) args[pcount++] + *in++; + break; + case '>': + if (args[pcount] > *in++) + { + args[pcount] += *in++; + } + else + { + in++; + } + break; + case 'r': + temp = args[pcount]; + args[pcount] = args[pcount + 1]; + args[pcount + 1] = temp; + break; + case 'i': + args[pcount]++; + args[pcount + 1]++; + break; + case '%': + *out++ = '%'; + break; + } +} diff --git a/src/tok.c b/src/tok.c new file mode 100644 index 0000000..fbb35ec --- /dev/null +++ b/src/tok.c @@ -0,0 +1,165 @@ +/* tok.c */ +/* +yylex() +sethard() +*/ +#if defined NIX +#include +#endif + +#include +#include +#include +#include + +#include "includes/larncons.h" +#include "includes/larndata.h" +#include "includes/larnfunc.h" +#include "includes/tok.h" +#include "includes/display.h" +#include "includes/io.h" +#include "includes/scores.h" + +#define CHKPTINT 400 + +static char lastok = 0; +int yrepcount = 0; +int move_no_pickup = FALSE; + + + +/* +* lexical analyzer for larn +*/ +int +yylex (void) +{ + char cc; + char firsttime = TRUE; + + if (hit2flag) + { + hit2flag = 0; + yrepcount = 0; + return (' '); + } + if (yrepcount > 0) + { + --yrepcount; + return (lastok); + } + else + yrepcount = 0; + if (yrepcount == 0) + { + bottomdo (); + showplayer (); /* show where the player is */ + move_no_pickup = FALSE; /* clear 'm' flag */ + } + + lflush (); + for (;;) + { + cdesc[BYTESIN]++; + + cc = ttgetch (); + + /* get repeat count, showing to player + */ + if ((cc <= '9') && (cc >= '0')) + { + yrepcount = yrepcount * 10 + cc - '0'; + + /* show count to player for feedback + */ + if (yrepcount >= 10) + { + cursors (); + if (firsttime) + lprcat ("\n"); + lprintf ("count: %d", (int) yrepcount); + firsttime = FALSE; + lflush (); /* show count */ + } + } + else + { + /* check for multi-character commands and handle. + */ + if (cc == 'm') + { + move_no_pickup = TRUE; + cc = ttgetch (); + } + if (yrepcount > 0) + --yrepcount; + return (lastok = cc); + } + } +} + + + + + +/* +function to set the desired hardness +enter with hard= -1 for default hardness, else any desired hardness +*/ +void +sethard (int hard) +{ + int j, k; + int i; + struct monst *mp; + + j = cdesc[HARDGAME]; + hashewon (); + + /* don't set cdesc[HARDGAME] if restoring game */ + if (restorflag == 0) + { + + if (hard >= 0) + { + + cdesc[HARDGAME] = hard; + } + + } + else + { + + /* set cdesc[HARDGAME] to proper value if restoring game */ + cdesc[HARDGAME] = j; + } + + k = cdesc[HARDGAME]; + + if (k == 0) + { + + return; + } + + for (j = 0; j <= MAXMONST + 8; j++) + { + + mp = &monster[j]; + + i = ((6 + k) * mp->hitpoints + 1) / 6; + mp->hitpoints = (i < 0) ? 32767 : i; + + i = ((6 + k) * mp->damage + 1) / 5; + mp->damage = (i > 127) ? 127 : i; + + i = (10 * mp->gold) / (10 + k); + mp->gold = (i > 32767) ? 32767 : i; + + i = mp->armorclass - k; + mp->armorclass = (i < -127) ? -127 : i; + + i = (7 * mp->experience) / (7 + k) + 1; + mp->experience = (i <= 0) ? 1 : i; + } +}