commit 63cff6654fcddee765211efefef392e70d2f1fae Author: TakeV Date: Thu Feb 23 12:53:09 2023 -0800 Initial commit diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..c48e460 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +# +# Makefile +# +# Copyright 2007, 2008 Lancer-X/ASCEAI +# +# This file is part of Meritous. +# +# Meritous is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Meritous is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Meritous. If not, see . +# +LDFLAGS = `sdl-config --libs` -lSDL_image -lSDL_mixer -lz +CCFLAGS = -O2 -Wall `sdl-config --cflags` -g +# +OBJS = src/levelblit.o \ + src/mapgen.o \ + src/demon.o \ + src/gamemap.o \ + src/tiles.o \ + src/save.o \ + src/help.o \ + src/audio.o \ + src/boss.o \ + src/ending.o +# +default: meritous + +%.o: %.c + gcc -c -o $@ $? ${CCFLAGS} + +meritous: ${OBJS} + gcc -o $@ $+ ${LDFLAGS} + +clean: + rm ${OBJS} + diff --git a/dat/a/circuitcharge.wav b/dat/a/circuitcharge.wav new file mode 100644 index 0000000..caf7d65 Binary files /dev/null and b/dat/a/circuitcharge.wav differ diff --git a/dat/a/circuitrecover.wav b/dat/a/circuitrecover.wav new file mode 100644 index 0000000..68b5fe5 Binary files /dev/null and b/dat/a/circuitrecover.wav differ diff --git a/dat/a/circuitrelease.wav b/dat/a/circuitrelease.wav new file mode 100644 index 0000000..2e58787 Binary files /dev/null and b/dat/a/circuitrelease.wav differ diff --git a/dat/a/crystal.wav b/dat/a/crystal.wav new file mode 100644 index 0000000..2dfaa04 Binary files /dev/null and b/dat/a/crystal.wav differ diff --git a/dat/a/crystal2.wav b/dat/a/crystal2.wav new file mode 100644 index 0000000..4316cbb Binary files /dev/null and b/dat/a/crystal2.wav differ diff --git a/dat/a/enemyhit.wav b/dat/a/enemyhit.wav new file mode 100644 index 0000000..5c59604 Binary files /dev/null and b/dat/a/enemyhit.wav differ diff --git a/dat/a/mons0shot.wav b/dat/a/mons0shot.wav new file mode 100644 index 0000000..cdd833d Binary files /dev/null and b/dat/a/mons0shot.wav differ diff --git a/dat/a/mons10shot.wav b/dat/a/mons10shot.wav new file mode 100644 index 0000000..8614156 Binary files /dev/null and b/dat/a/mons10shot.wav differ diff --git a/dat/a/mons1shot.wav b/dat/a/mons1shot.wav new file mode 100644 index 0000000..73a93fd Binary files /dev/null and b/dat/a/mons1shot.wav differ diff --git a/dat/a/mons2shot.wav b/dat/a/mons2shot.wav new file mode 100644 index 0000000..9165010 Binary files /dev/null and b/dat/a/mons2shot.wav differ diff --git a/dat/a/mons3shot.wav b/dat/a/mons3shot.wav new file mode 100644 index 0000000..24e9bf1 Binary files /dev/null and b/dat/a/mons3shot.wav differ diff --git a/dat/a/mons4shot.wav b/dat/a/mons4shot.wav new file mode 100644 index 0000000..3fcde50 Binary files /dev/null and b/dat/a/mons4shot.wav differ diff --git a/dat/a/mons5shot.wav b/dat/a/mons5shot.wav new file mode 100644 index 0000000..809b726 Binary files /dev/null and b/dat/a/mons5shot.wav differ diff --git a/dat/a/mons6shot.wav b/dat/a/mons6shot.wav new file mode 100644 index 0000000..39226a3 Binary files /dev/null and b/dat/a/mons6shot.wav differ diff --git a/dat/a/mons7shot.wav b/dat/a/mons7shot.wav new file mode 100644 index 0000000..00d525e Binary files /dev/null and b/dat/a/mons7shot.wav differ diff --git a/dat/a/mons8shot.wav b/dat/a/mons8shot.wav new file mode 100644 index 0000000..9165010 Binary files /dev/null and b/dat/a/mons8shot.wav differ diff --git a/dat/a/mons9shot.wav b/dat/a/mons9shot.wav new file mode 100644 index 0000000..2b34ae5 Binary files /dev/null and b/dat/a/mons9shot.wav differ diff --git a/dat/a/playerhurt.wav b/dat/a/playerhurt.wav new file mode 100644 index 0000000..04b368b Binary files /dev/null and b/dat/a/playerhurt.wav differ diff --git a/dat/a/shieldhit.wav b/dat/a/shieldhit.wav new file mode 100644 index 0000000..04d37fd Binary files /dev/null and b/dat/a/shieldhit.wav differ diff --git a/dat/a/teleport.wav b/dat/a/teleport.wav new file mode 100644 index 0000000..63d15f1 Binary files /dev/null and b/dat/a/teleport.wav differ diff --git a/dat/a/tone.wav b/dat/a/tone.wav new file mode 100644 index 0000000..a839d17 Binary files /dev/null and b/dat/a/tone.wav differ diff --git a/dat/d/bossroom.loc b/dat/d/bossroom.loc new file mode 100644 index 0000000..755ab0c --- /dev/null +++ b/dat/d/bossroom.loc @@ -0,0 +1 @@ +7676767676767676766!!!!88888888!!!!77!"#!87676768!"#!66!$%!86767678!$%!77!!!!87676768!!!!66888886767678888877676767676767676766888886767678888877!!!!87676768!!!!66!"#!86767678!"#!77!$%!87676768!$%!66!!!!88888888!!!!7767676767676767676 \ No newline at end of file diff --git a/dat/d/centre.loc b/dat/d/centre.loc new file mode 100644 index 0000000..26b4c71 Binary files /dev/null and b/dat/d/centre.loc differ diff --git a/dat/d/cstream.loc b/dat/d/cstream.loc new file mode 100644 index 0000000..e6d8e0b Binary files /dev/null and b/dat/d/cstream.loc differ diff --git a/dat/d/fbossroom.loc b/dat/d/fbossroom.loc new file mode 100644 index 0000000..2596904 Binary files /dev/null and b/dat/d/fbossroom.loc differ diff --git a/dat/d/font.dat b/dat/d/font.dat new file mode 100644 index 0000000..ee3809c Binary files /dev/null and b/dat/d/font.dat differ diff --git a/dat/d/helpfile.txt b/dat/d/helpfile.txt new file mode 100644 index 0000000..d013615 --- /dev/null +++ b/dat/d/helpfile.txt @@ -0,0 +1,385 @@ +'_____________________________________________________________________ +:default +!--------------------------------------------------------------------- + +!MERITOUS HELP FILE + +!--------------------------------------------------------------------- + +To navigate this help file, use the up and down arrow keys. Press +ENTER to activate links. Press ESC to return to the game. You can +access this help file at any time by pressing 'H'. + +!Table of Contents + +?intro?Introduction +?background?Background +?controls?Controls +?wuss?Wuss Mode +?dungeon?The Dome +?circuit?Your PSI Circuit and You +?cred?Credits +:intro +!--------------------------------------------------------------------- + +!Introduction + +!--------------------------------------------------------------------- + + " Far below the surface of the planet is a secret. + A place of limitless power. Those that seek to + control such a utopia will soon bring an end + to themselves. " + +MERITOUS is a top-view dungeon crawl action/adventure game. As MERIT, +a PSI user, your task is to investigate the Orcus Dome, an ancient +underground shrine. + +In your way are the shadowy creatures that wander the shrine. Using +your PSI circuit, you must cut through any that come between you and +your goal. + +?background?Background +?controls?Controls +?dungeon?The Dome + +?default?Return to top +:background +!--------------------------------------------------------------------- + +!Background + +!--------------------------------------------------------------------- + +!-- PSI -- + +PSI is a powerful, imprecise force. Often viewed as a form of modern +magic, PSI can be weilded to a limited extent by beings (see PSI +users) that have an unusual level of mental awareness. PSI exists as +a flow that can be fashioned into a single-use tool for the weilder to +use. + +As PSI is very difficult to shape precisely, PSI users often +settle for crude, uneconomical solutions to problems. For example, +using PSI as an attacking weapon by draining off a large amount of it +from the surrounding area and unleashing it in one go. Or using PSI +as a protective shield by concentrating a large amount of it in one +area. + +Where PSI actually comes from is unknown, however there seems to be +only a limited amount in the atmosphere at any one time. While PSI is +generally used by gathering it from the area around the PSI user, some +advanced PSI users have worked out ways of gathering and storing large +quantities of it. + +Some areas are naturally much richer in PSI than others. PSI seems to +be found in greater quantities when one is underground, for example. + +!-- PSI users -- + +PSI users are beings that have the mental capacity to consciously use +PSI. Any species is capable of producing PSI users, though by far +human PSI users are the most common. + +PSI users vary wildly in ability, with the rarest and most powerful +able to accomplish great feats. It is also not uncommon for PSI users +to become more or less powerful over time. Theories exist that +continual exposure to and use of PSI increases one's competence, while +disuse can cause it to stagnate. + +!-- Orcus Dome -- + +The Orcus Dome is a structure located deep within the planet's crust. +The internal structure seems to be both crafted and organically grown. +It is made up of a number of tightly-woven rooms. The structure is +unusual in that it seems to have been made for the precise purpose of +gathering and concentrating PSI. The walls that divide the rooms +carry PSI gathered from the planet and channel it into a single, very +strong flow of PSI. + +The discovery of the Dome has been a point of contention among PSI +users, most of whom want to use the large reserves of the Dome to +enhance their own PSI usage. + +Eventually, the majority of PSI users agreed to ration out use of the +Dome to both ensure the existing reserves would last as long as +possible and to make sure that each PSI user would receive a fair +allocation. + +!-- Strange events -- + +For several years, there was no problem with the ongoing use of the +Orcus Dome to provide PSI. However, for some reason, the PSI extracted +from the Dome began to behave strangely. It worked unpredictably, and +made controlling the PSI very difficult. After one PSI user was killed +as a result of this, a decision was made to abandon using the Orcus +Dome for PSI. + +Not happy with this decision, one PSI user, MERIT, decided to use his +own reserves of PSI to ethereally warp into the Dome itself to find +out what the problem was and restore the Dome to its original state. + +?default?Return to top +:controls +!--------------------------------------------------------------------- + +!Controls + +!--------------------------------------------------------------------- + +Arrow keys - Move around. To walk through doors, simply walk up to + them and push against them. + +Space - Charge your PSI circuit. + +Escape - Exit the game. + +H - View the help file. + +Tab - View the map (you can then use the arrow keys to scroll + around the map. + +Enter - Activate a trigger tile that you are standing on. + Enter is also used for various other things, such as + for reading in-game dialogue. + +P - Pause the game. + +?default?Return to top +:dungeon +!--------------------------------------------------------------------- + +!The Dome + +!--------------------------------------------------------------------- + +The Orcus Dome is made up of a series of rooms, connected with short +passages. The rooms are mostly uniform in design, however, there are +some points of interest to look out for. + +!---------------------------------- +!Trigger tiles +!---------------------------------- + +Trigger tiles are special tiles that cover the floor of the dungeon, +and can be activated by standing on top of them and pressing Enter. + +!Checkpoint tiles + +Checkpoint tiles automatically record your genetic makeup when you +enter the room that they are in, and, at the cost of some of your +natural vitality, will restore your being if you perish within the +Dome. + +The process can also be used to warp yourself between tiles with no +cost, by pressing Enter while on one of them. + +!PSI Enhancement tiles + +These tiles will, at the cost of some of your PSI crystals, enhance +your PSI abilities in one of three different areas. + +More on PSI abilities can be found in the chapter 'Your PSI Circuit +and You' + +!Crystal Magnet tiles + +These tiles, upon activation, pull loose PSI crystals into the room +that the tile is in. + +!Compass tiles + +These tiles point out useful places for you to go to. If nothing +appears when you stand on the tile, there is nothing for you to do +at the moment until more shadow creatures are destroyed. + +!Save tiles + +These tiles use their own supply of PSI to record your current +state, allowing you to come back to the Dome if you decide to leave +for a while. They do this upon activation with the Enter key. + +?circuit?Your PSI Circuit and You + +!---------------------------------- +!PSI Artifacts +!---------------------------------- + +There are a number of artifacts constructed with PSI lying around the +Dome, and some of these will be crucial to your success. You can +locate these with the compass tile. + +!Mystical Enhancement Tools + +These artifacts will assist you in exploring the Dome by enhancing +your current abilities. With the right artifacts, you can move faster, +regenerate your shields in less time, see shadow creatures through +walls, increase the power of your PSI attack and several other things. + +These artifacts are usually found in chests, and are often well +protected, but you can find them with the compass tiles. + +!PSI Keys + +In the shape of famous weapons, the Keys are responsible for filtering +and controlling the flow of PSI throughout the Dome. They are placed +at the points where ley-lines intersect, and it is unknown what will +happen if they are moved. You can find them, even if they have been +moved, with the compass tiles, but you will not be able to touch them +if they have been corrupted with darkness. + +!Ancient Seal + +One of the artifacts, in the shape of a pendant, must be kept at the +centre of the Dome at all times to keep the PSI free of malign +influences. The Ancient Seal can also be found with the compass tiles, +but will only be visible while the PSI Keys are in place. + +?default?Return to top +:circuit +!--------------------------------------------------------------------- + +!Your PSI Circuit and You + +!--------------------------------------------------------------------- + +Your PSI ability has two main purposes in the game--offence and +defence. + +!Offence + +By holding down the spacebar, you can charge your offensive PSI +circuit and unleash it on surrounding foes. Creatures made up of PSI +that are hit by this force will crystallise and break down into PSI +crystals that can be collected. + +There are two different targeting reticles used while the PSI circuit +is charging. The circle will appear over enemies that are in range +of your attack. The cross will appear over enemies that your PSI +circuit is charged enough to destroy. Both of these should appear over +your intended enemies before you release the charge. Only then can you +ensure that they will be hit. + +Beware, however--some enemies can take more than one hit from the PSI +circuit before they are destroyed. Some more powerful creatures may +not show a targeting reticle at all. In those cases you will have to +experiment to find out how much power you need to destroy them. + +The further you charge your PSI circuit, the longer it will take for +your circuit to recover enough to use it again. For this reason it is +rarely a good idea to keep your circuit fully charged. + +Most enemy attacks can be stopped prematurely using the PSI circuit, +and enemy bullets can be knocked out of the air. + +Your 'Circuit charge' and 'Circuit refill' skills affect how quickly +your offensive PSI circuit charges and recovers respectively. In +addition, upgrading these values also increases the maximum charge +possible for your circuit, necessary for destroying the most powerful +enemies. These can be upgraded by standing on the necessary enhance +tiles and pressing Enter when you have sufficient PSI crystals. + +!Defence + +If your 'Reflect shield' skill is 1 or higher, you will automatically +use your PSI to form a physical shield around yourself, which can +block out attacks to a limited extent. For every point of skill you +have, your shield will be able to block out one enemy bullet before +it recharges. The higher your skill, the faster the shield will +recharge. Note that particularly powerful attacks, such as lasers, +are able to blast through multiple levels of shield in one go. + +A shield is absolutely necessary for facing the more powerful enemies +in the Dome, so it is advised that you upgrade your shield when you +can. + +!Further notes on enhancing skills + +Because it costs more crystals to enhance a skill when you have more +ability in that skill, it is more efficient to keep your three skills +balanced rather than trying to maximise one of them. + +On skill display at the top of the screen, the name of the skill will +glow when you have enough crystals to upgrade that skill on an enhance +tile. + +!PSI recollection + +Your PSI also enhances your ability to recall facts and visualise them +accurately. As a result of this, you will remember the layout of the +portions of the Dome you have explored so far, and can visualise this +information by pressing Tab. This will show a map that you can scroll +around, and can be very useful for finding your way around the Dome. + +!PSI vitality + +Last, but not least, your PSI also maintains your body vitality. +In the top-right corner, the three hearts represent this. If you lose +all three you will die and be regenerated at the last checkpoint. + +Unless you are playing on Wuss mode, you will also have a limited +number of lives. This represents your physical vitality, and it will +be damaged each time you die. When you run out, your body will be +permanently destroyed and you won't be regenerated. + +You can improve your vitality by collecting PSI crystals that have +formed into a heart shape. One of these will restore one of your +three hearts. If you already have three hearts, they will go towards +improving your physical vitality. Collect enough and your number of +lives will increase. + +?default?Return to top +:wuss +!--------------------------------------------------------------------- + +!Wuss Mode + +!--------------------------------------------------------------------- + +Available from the title menu, Wuss mode is a reduced-difficulty +gameplay mode. When playing Wuss mode, you will be able to attack +faster, enemy bullets will be slower and you will never permanently +die. However, a victory in Wuss mode is not considered to be an +actual completion of the game, and as a result the game will not end +the same way that it would normally. + +?default?Return to top +:cred +!--------------------------------------------------------------------- + +!Credits + +!--------------------------------------------------------------------- + + Concept: Lancer-X/Asceai + + Game design: Lancer-X/Asceai + + Graphics: Lancer-X/Asceai + + Programming: Lancer-X/Asceai + + Sound Effects: Various sources + + Beta testing: Quasar + + Beta testing: Terryn + + Beta testing: Wervyn + + Music: + "Ambient Light" Vogue of Triton + "Battle of Ragnarok" Frostbite + "Dragon Cave" TICAZ + cavern.xm Unknown + "Caverns Boss" Alexis Janson + "Forest Boss" Alexis Janson + "Catacombs Boss" Alexis Janson + "Fear 2" Mick Rippon + "The Final Battle" Goose/CéDA & iNVASiON + "Ice Frontier" Skaven/FC + "KnarkLoader 1.0" Rapacious + "RPG-Battle" Cyn + "Metallic Forest" Joseph Fox + +?default?Return to top \ No newline at end of file diff --git a/dat/d/icon_bitmask.dat b/dat/d/icon_bitmask.dat new file mode 100644 index 0000000..63d448a Binary files /dev/null and b/dat/d/icon_bitmask.dat differ diff --git a/dat/d/weapon.loc b/dat/d/weapon.loc new file mode 100644 index 0000000..e9d4bbc Binary files /dev/null and b/dat/d/weapon.loc differ diff --git a/dat/i/agate.png b/dat/i/agate.png new file mode 100644 index 0000000..e7c0282 Binary files /dev/null and b/dat/i/agate.png differ diff --git a/dat/i/artifacts.png b/dat/i/artifacts.png new file mode 100644 index 0000000..a0859c5 Binary files /dev/null and b/dat/i/artifacts.png differ diff --git a/dat/i/asceai.png b/dat/i/asceai.png new file mode 100644 index 0000000..0d49fca Binary files /dev/null and b/dat/i/asceai.png differ diff --git a/dat/i/automap.png b/dat/i/automap.png new file mode 100644 index 0000000..6cd82ec Binary files /dev/null and b/dat/i/automap.png differ diff --git a/dat/i/boss1.png b/dat/i/boss1.png new file mode 100644 index 0000000..b28212a Binary files /dev/null and b/dat/i/boss1.png differ diff --git a/dat/i/boss2.png b/dat/i/boss2.png new file mode 100644 index 0000000..2ae3297 Binary files /dev/null and b/dat/i/boss2.png differ diff --git a/dat/i/boss3.png b/dat/i/boss3.png new file mode 100644 index 0000000..a9622d5 Binary files /dev/null and b/dat/i/boss3.png differ diff --git a/dat/i/boss4.png b/dat/i/boss4.png new file mode 100644 index 0000000..4f9d8ca Binary files /dev/null and b/dat/i/boss4.png differ diff --git a/dat/i/boss_icon.png b/dat/i/boss_icon.png new file mode 100644 index 0000000..5707cbd Binary files /dev/null and b/dat/i/boss_icon.png differ diff --git a/dat/i/circuits_1.png b/dat/i/circuits_1.png new file mode 100644 index 0000000..9ba6496 Binary files /dev/null and b/dat/i/circuits_1.png differ diff --git a/dat/i/fin.png b/dat/i/fin.png new file mode 100644 index 0000000..bf83a76 Binary files /dev/null and b/dat/i/fin.png differ diff --git a/dat/i/gem.png b/dat/i/gem.png new file mode 100644 index 0000000..8afad92 Binary files /dev/null and b/dat/i/gem.png differ diff --git a/dat/i/glitter.png b/dat/i/glitter.png new file mode 100644 index 0000000..5a16cd5 Binary files /dev/null and b/dat/i/glitter.png differ diff --git a/dat/i/hidden_monster.png b/dat/i/hidden_monster.png new file mode 100644 index 0000000..e074c41 Binary files /dev/null and b/dat/i/hidden_monster.png differ diff --git a/dat/i/icon.png b/dat/i/icon.png new file mode 100644 index 0000000..2372bfb Binary files /dev/null and b/dat/i/icon.png differ diff --git a/dat/i/inrange.png b/dat/i/inrange.png new file mode 100644 index 0000000..aad441e Binary files /dev/null and b/dat/i/inrange.png differ diff --git a/dat/i/linetest.png b/dat/i/linetest.png new file mode 100644 index 0000000..8cfcfbf Binary files /dev/null and b/dat/i/linetest.png differ diff --git a/dat/i/meter.png b/dat/i/meter.png new file mode 100644 index 0000000..6d3f112 Binary files /dev/null and b/dat/i/meter.png differ diff --git a/dat/i/mons1.png b/dat/i/mons1.png new file mode 100644 index 0000000..b71f147 Binary files /dev/null and b/dat/i/mons1.png differ diff --git a/dat/i/mons10.png b/dat/i/mons10.png new file mode 100644 index 0000000..fd2587f Binary files /dev/null and b/dat/i/mons10.png differ diff --git a/dat/i/mons2.png b/dat/i/mons2.png new file mode 100644 index 0000000..b215a2b Binary files /dev/null and b/dat/i/mons2.png differ diff --git a/dat/i/mons3.png b/dat/i/mons3.png new file mode 100644 index 0000000..4e67f02 Binary files /dev/null and b/dat/i/mons3.png differ diff --git a/dat/i/mons4.png b/dat/i/mons4.png new file mode 100644 index 0000000..75efa51 Binary files /dev/null and b/dat/i/mons4.png differ diff --git a/dat/i/mons5.png b/dat/i/mons5.png new file mode 100644 index 0000000..f640ccc Binary files /dev/null and b/dat/i/mons5.png differ diff --git a/dat/i/mons6.png b/dat/i/mons6.png new file mode 100644 index 0000000..bf1ff3f Binary files /dev/null and b/dat/i/mons6.png differ diff --git a/dat/i/mons7.png b/dat/i/mons7.png new file mode 100644 index 0000000..d7afc2c Binary files /dev/null and b/dat/i/mons7.png differ diff --git a/dat/i/mons8.png b/dat/i/mons8.png new file mode 100644 index 0000000..8028e69 Binary files /dev/null and b/dat/i/mons8.png differ diff --git a/dat/i/mons9.png b/dat/i/mons9.png new file mode 100644 index 0000000..25b0f3e Binary files /dev/null and b/dat/i/mons9.png differ diff --git a/dat/i/overview.png b/dat/i/overview.png new file mode 100644 index 0000000..948794a Binary files /dev/null and b/dat/i/overview.png differ diff --git a/dat/i/player.png b/dat/i/player.png new file mode 100644 index 0000000..86028cc Binary files /dev/null and b/dat/i/player.png differ diff --git a/dat/i/reticle.png b/dat/i/reticle.png new file mode 100644 index 0000000..cd03471 Binary files /dev/null and b/dat/i/reticle.png differ diff --git a/dat/i/star1.png b/dat/i/star1.png new file mode 100644 index 0000000..fc7bb5f Binary files /dev/null and b/dat/i/star1.png differ diff --git a/dat/i/star2.png b/dat/i/star2.png new file mode 100644 index 0000000..1522381 Binary files /dev/null and b/dat/i/star2.png differ diff --git a/dat/i/star3.png b/dat/i/star3.png new file mode 100644 index 0000000..3aeae34 Binary files /dev/null and b/dat/i/star3.png differ diff --git a/dat/i/stream.png b/dat/i/stream.png new file mode 100644 index 0000000..b5ba541 Binary files /dev/null and b/dat/i/stream.png differ diff --git a/dat/i/teleflash.png b/dat/i/teleflash.png new file mode 100644 index 0000000..78d678a Binary files /dev/null and b/dat/i/teleflash.png differ diff --git a/dat/i/theend.png b/dat/i/theend.png new file mode 100644 index 0000000..f535b73 Binary files /dev/null and b/dat/i/theend.png differ diff --git a/dat/i/tileset.png b/dat/i/tileset.png new file mode 100644 index 0000000..ac9b11a Binary files /dev/null and b/dat/i/tileset.png differ diff --git a/dat/i/title.png b/dat/i/title.png new file mode 100644 index 0000000..802916e Binary files /dev/null and b/dat/i/title.png differ diff --git a/dat/i/true_end.png b/dat/i/true_end.png new file mode 100644 index 0000000..adcd129 Binary files /dev/null and b/dat/i/true_end.png differ diff --git a/dat/i/wuss_ending.png b/dat/i/wuss_ending.png new file mode 100644 index 0000000..1c7a048 Binary files /dev/null and b/dat/i/wuss_ending.png differ diff --git a/dat/m/CT_BOSS.MOD b/dat/m/CT_BOSS.MOD new file mode 100644 index 0000000..6909215 Binary files /dev/null and b/dat/m/CT_BOSS.MOD differ diff --git a/dat/m/Cv_boss.mod b/dat/m/Cv_boss.mod new file mode 100644 index 0000000..da674e4 Binary files /dev/null and b/dat/m/Cv_boss.mod differ diff --git a/dat/m/FINALBAT.s3m b/dat/m/FINALBAT.s3m new file mode 100644 index 0000000..f0086ab Binary files /dev/null and b/dat/m/FINALBAT.s3m differ diff --git a/dat/m/Fr_boss.mod b/dat/m/Fr_boss.mod new file mode 100644 index 0000000..abb637d Binary files /dev/null and b/dat/m/Fr_boss.mod differ diff --git a/dat/m/ICEFRONT.S3M b/dat/m/ICEFRONT.S3M new file mode 100644 index 0000000..d7ac6ef Binary files /dev/null and b/dat/m/ICEFRONT.S3M differ diff --git a/dat/m/Wood.s3m b/dat/m/Wood.s3m new file mode 100644 index 0000000..718ae32 Binary files /dev/null and b/dat/m/Wood.s3m differ diff --git a/dat/m/amblight.xm b/dat/m/amblight.xm new file mode 100644 index 0000000..2ece4d6 Binary files /dev/null and b/dat/m/amblight.xm differ diff --git a/dat/m/cave.xm b/dat/m/cave.xm new file mode 100644 index 0000000..3235bc9 Binary files /dev/null and b/dat/m/cave.xm differ diff --git a/dat/m/cave06.s3m b/dat/m/cave06.s3m new file mode 100644 index 0000000..77784e6 Binary files /dev/null and b/dat/m/cave06.s3m differ diff --git a/dat/m/cavern.xm b/dat/m/cavern.xm new file mode 100644 index 0000000..dc1b414 Binary files /dev/null and b/dat/m/cavern.xm differ diff --git a/dat/m/fear2.mod b/dat/m/fear2.mod new file mode 100644 index 0000000..6809752 Binary files /dev/null and b/dat/m/fear2.mod differ diff --git a/dat/m/iller_knarkloader_final.xm b/dat/m/iller_knarkloader_final.xm new file mode 100644 index 0000000..f643fba Binary files /dev/null and b/dat/m/iller_knarkloader_final.xm differ diff --git a/dat/m/rpg_bat1.xm b/dat/m/rpg_bat1.xm new file mode 100644 index 0000000..e7f5ffc Binary files /dev/null and b/dat/m/rpg_bat1.xm differ diff --git a/gpl.txt b/gpl.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/gpl.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..2ac5860 --- /dev/null +++ b/readme.txt @@ -0,0 +1,53 @@ +MERITOUS v 1.2 +for Windows 98/2K/XP/other operating systems +by Lancer-X/ASCEAI + +Far below the surface of the planet is a secret. +A place of limitless power. Those that seek to +control such a utopia will soon bring an end +to themselves. + +Seeking an end to the troubles that plague him, +PSI user MERIT journeys into the hallowed Orcus +Dome in search of answers. + + +INSTALLATION: + +Run Meritous.exe after extraction and everything should work correctly. + +If the game is too difficult for you, you can play in 'Wuss mode' for +a far more sombre gaming experience. + + +BASIC CONTROLS: + +Arrow keys - Move around. To walk through doors, simply walk up to + them and push against them. + +Space - Charge your PSI circuit for attacking. + +Escape - Exit the game. + +H - View the help file. + +Tab - View the map (you can then use the arrow keys to scroll + around the map. + +Enter - Activate a trigger tile that you are standing on. + Enter is also used for various other things, such as + for reading in-game dialogue. + +P - Pause the game. + + +COMMENTS: + +You can send comments/questions/whatever to my email address, which is +given on the website. + + +ACKNOWLEDGEMENTS: + +Thanks to my beta testers: Quasar, Terryn and Wervyn, for their hard work +in assisting me in keeping Meritous fairly stable. diff --git a/src/audio.c b/src/audio.c new file mode 100755 index 0000000..0e86dd2 --- /dev/null +++ b/src/audio.c @@ -0,0 +1,240 @@ +// +// audio.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include +#include +#include + +#include "levelblit.h" +#include "mapgen.h" +#include "boss.h" + +char *tracks[13] = {"dat/m/ICEFRONT.S3M", + "dat/m/cavern.xm", + "dat/m/cave.xm", + "dat/m/cave06.s3m", + "dat/m/Wood.s3m", + "dat/m/iller_knarkloader_final.xm", + "dat/m/fear2.mod", + "dat/m/Cv_boss.mod", + "dat/m/Fr_boss.mod", + "dat/m/CT_BOSS.MOD", + "dat/m/rpg_bat1.xm", + "dat/m/amblight.xm", + "dat/m/FINALBAT.s3m"}; + +Mix_Music *bgm_music = NULL; +int bgm_track = -1; + +Mix_Chunk *c_sample[16] = {NULL}; + +void InitAudio() +{ + Mix_OpenAudio(44100, AUDIO_S16, 2, 4096); + Mix_VolumeMusic(112); + Mix_AllocateChannels(16); +} + +void BackgroundMusic(); +void CircuitHum(); + +void MusicUpdate() +{ + BackgroundMusic(); + CircuitHum(); +} + +void CircuitHum() +{ + int hum_vol = 0; + static int hum_play = 0; + + if (magic_circuit == 0) { + hum_vol = 0; + if (hum_play != 0) { + Mix_HaltChannel(0); + hum_play = 0; + } + } + if (magic_circuit > 0) { + hum_vol = (circuit_size * 24 / 3000)+12; + hum_vol = (magic_circuit * 96 / circuit_size) * hum_vol / 36 + 32; + + if (hum_play != 1) { + if (hum_play == 2) { + Mix_HaltChannel(0); + Mix_FreeChunk(c_sample[0]); + } + c_sample[0] = Mix_LoadWAV("dat/a/circuitcharge.wav"); + Mix_PlayChannel(0, c_sample[0], -1); + hum_play = 1; + } + } + if (magic_circuit < 0) { + hum_vol = (circuit_size - (magic_circuit + circuit_size)) * 80 / 3000; + if (hum_play != 2) { + if (hum_play == 1) { + Mix_HaltChannel(0); + Mix_FreeChunk(c_sample[0]); + } + c_sample[0] = Mix_LoadWAV("dat/a/circuitrecover.wav"); + Mix_PlayChannel(0, c_sample[0], -1); + hum_play = 2; + } + } + + Mix_Volume(0, hum_vol); +} + +void TitleScreenMusic() +{ + int new_track = 5; + if (bgm_track != -1) { + Mix_HaltMusic(); + Mix_FreeMusic(bgm_music); + bgm_music = NULL; + } + + bgm_music = Mix_LoadMUS(tracks[new_track]); + Mix_PlayMusic(bgm_music, -1); + bgm_track = new_track; +} + +void BackgroundMusic() +{ + int new_track = -1; + Mix_VolumeMusic(96); + + if (rooms[player_room].s_dist <= 15) { + new_track = 0; + } + + if (bgm_track == 1) { + if ((rooms[player_room].s_dist <= 30) && (rooms[player_room].s_dist >= 10)) { + new_track = 1; + } + } + if (bgm_track == 2) { + if ((rooms[player_room].s_dist <= 45) && (rooms[player_room].s_dist >= 25)) { + new_track = 2; + } + } + + if (new_track == -1) { + if ((rooms[player_room].s_dist <= 30) && (rooms[player_room].s_dist >= 16)) { + new_track = 1; + } + if ((rooms[player_room].s_dist <= 39) && (rooms[player_room].s_dist >= 31)) { + new_track = 2; + } + if (rooms[player_room].s_dist >= 40) { + new_track = 3; + } + } + + if (rooms[player_room].room_type == 3) { + new_track = 4; + } + + if (artifacts[11]) { + new_track = 6; + } + + if (rooms[player_room].room_type == 2) { + if (boss_fight_mode == 2) { + if ( (current_boss == 3) && (player_shield == 30) ) { + if (boss_lives <= 1) { + new_track = 7; + } else { + new_track = 12; + } + } else { + new_track = 7 + current_boss; + } + Mix_VolumeMusic(128); + } else { + new_track = -1; + } + } + + if ( (player_room == 0) && (current_boss == 3) && (boss_fight_mode >= 3) ) { + new_track = 11; + } + + if (new_track == bgm_track) return; + + if (bgm_track != -1) { + Mix_HaltMusic(); + Mix_FreeMusic(bgm_music); + bgm_music = NULL; + } + if (new_track != -1) { + bgm_music = Mix_LoadMUS(tracks[new_track]); + Mix_PlayMusic(bgm_music, -1); + } + bgm_track = new_track; +} + +int SND_GetChannel() +{ + int i; + + for (i = 8; i < 16; i++) { + if (Mix_Playing(i) == 0) { + if (c_sample[i] != NULL) { + Mix_FreeChunk(c_sample[i]); + c_sample[i] = NULL; + } + return i; + } + } + return -1; +} + +void SND_Play(char *filename, int vol) +{ + int ch; + + ch = SND_GetChannel(); + if (ch != -1) { + c_sample[ch] = Mix_LoadWAV(filename); + Mix_Volume(ch, vol); + Mix_PlayChannel(ch, c_sample[ch], 0); + } +} + +void SND_Pos(char *filename, int vol, int dist) +{ + int real_vol; + if (dist > 1600) return; + + real_vol = vol * (40 - (sqrt(dist))) / 40; + SND_Play(filename, real_vol); +} + +void SND_CircuitRelease(int str) +{ + SND_Play("dat/a/circuitrelease.wav", sqrt(str * 2 + (str * 5300 / circuit_size) + (str > 100 ? 5300 : str*53))); +} diff --git a/src/audio.h b/src/audio.h new file mode 100755 index 0000000..21f6e00 --- /dev/null +++ b/src/audio.h @@ -0,0 +1,33 @@ +// +// audio.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// Exposes audio.c functionality and types + +#ifndef AUDIO_H +#define AUDIO_H + +void InitAudio(); +void MusicUpdate(); +void TitleScreenMusic(); +void SND_CircuitRelease(int str); +void SND_Pos(char *filename, int vol, int dist); + +#endif diff --git a/src/boss.c b/src/boss.c new file mode 100755 index 0000000..a1423cd --- /dev/null +++ b/src/boss.c @@ -0,0 +1,1829 @@ +// +// boss.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include +#include +#include + +#include "levelblit.h" +#include "mapgen.h" +#include "demon.h" +#include "gamemap.h" +#include "audio.h" + +char *boss_names[] = { "MERIDIAN", + "ATARAXIA", + "MERODACH", + "WERVYN ANIXIL" }; + +int boss_fight_mode = 0; +// 0 - no boss fight +// 1 - entering boss room +// 2 - fiting teh boss +// 3 - boss dying +// 4-23 - final boss dying sequence + +int current_boss_room = 0; +int current_boss = 0; +int boss_x, boss_y; +int boss_hp; +int boss_lives; +int boss_tail_len = 0; +int boss_breakpoint; +int boss_flash = 0; +int boss_new_life = 0; +int boss_ox, boss_oy; +int boss_dlg = 0; +int resetboss = 0; +int final_boss_dlg = 0; +float boss_dmgrate = 1; +float boss_dir; + +int proxy_seek = 0; + +int boss_m_heads; +int boss_m_hx[4]; +int boss_m_hy[4]; +float boss_m_hd[4]; + +float boss_2h_dir = 0; +int boss_2h_dst = 64; + +int percent_required[4] = {15, 40, 60, 75}; + +char *artifact_names[] = { "Holy Sword 'Balmung'", + "Mystic Halberd 'Amenonuhoko'", + "Divine Bow 'Gandiva'", + "Cursed Seal of Yahveh"}; + +int CanGetArtifact(); +void DrawArtifactOverhead(int p_obj); + +struct dlg_box { + int pos; + int lines; + int last; + char *txt; +}; + +struct dlg_box dtext[5][12] = +// "This string of text is precisely seventy eight characters in length, usefully." +{ + { // Meridian + {0, 5, 0, + "MERIDIAN:\n" + " \n" + " Different...\n" + " \n" + " Your PSI is different. Your magic is perverse. Poisonous. Treacherous." + }, + {1, 5, 0, + "MERIT:\n" + " \n" + " Nothing but darkness carried on the wind of a curse. It's amazing that you\n" + " are able to communicate with me at all. Surely a statement on the perversity\n" + " of my magic, coming from you, is more than a little bit hypocritical." + }, + {0, 6, 1, + "MERIDIAN:\n" + " \n" + " So. You've even brought that tainted thing with you.\n" + " That does not really surprise me, but nonetheless, your weak, impure PSI\n" + " will do you little good against one manifest of shadows. Pitiful thing from\n" + " the surface, MERIDIAN shall return you to the dust from whence you came!" + } + }, + + { // Ataraxia + {0, 5, 0, + "ATARAXIA:\n" + " \n" + " It felt nearly as inconsequential as a stilling in the breeze, but there was\n" + " more to it, wasn't it? It feels as though I lost a part of myself. Was that\n" + " your doing?" + }, + {1, 6, 0, + "MERIT:\n" + " \n" + " Moving Ochra's Keys from the ley lines was only the start, am I correct?\n" + " Nonetheless, what you've already caused is enough. The Keys were placed to\n" + " prevent corruption in the PSI. Moving them renders the PSI unusable. There\n" + " is nothing to be gained from THAT." + }, + {0, 8, 0, + "ATARAXIA:\n" + " \n" + " Statements like that belie your youthful naivety and ignorance. There is no\n" + " way to make magic unusable. What you see now is the other side of PSI that\n" + " pitiful beings such as yourself turn a blind eye to. Like the sun at dawn\n" + " the sun at dusk, it is nonetheless the same sun, and each beautiful in its\n" + " own way. PSI isn't just for things like you, but for the entire cosmos.\n" + " Nothing can be gained by preventing it from taking its natural course." + }, + {1, 8, 0, + "MERIT:\n" + " \n" + " You delude yourself with the crazed whisperings from the muck that spawned\n" + " you. You say that this PSI is still PSI, but it is not. PSI is the energy of\n" + " creation. This is the energy of destruction. PSI is a pure force, gathered\n" + " from around me and targeted with my mind. This is a foul mixture, existing\n" + " as half-mad corruptions like you. It can only break apart when confronted\n" + " with true power." + }, + {0, 6, 1, + "ATARAXIA:\n" + " \n" + " You realise that not even your fellow PSI users agree with you? But, it is\n" + " of little consequence. I'll show you the true beauty of that which you shun\n" + " by smothering you in it and turning your flesh into fuel for the PSI that\n" + " runs through the veins of this Dome!" + } + }, + + { // Merodach + {0, 9, 0, + "MERODACH:\n" + " \n" + " e v e r y t h i n g h a s t u r n e d t o d a r k n e s s\n" + " \n" + " n o s o u n d n o l i g h t n o s e n s a t i o n\n" + " \n" + " w a s t h i s w h a t y o u w e r e a f t e r f r o m\n" + " \n" + " t h e b e g i n n i n g ?" + }, + {0, 7, 1, + "MERODACH\n" + " \n" + " i ' l l w r a p m y r e m a i n i n g P S I a r o u n d y o u\n" + " \n" + " a n d e x t i n g u i s h t h a t l i f e o f y o u r s\n" + " \n" + " l e t ' s s e e h o w y o u e n j o y e t e r n a l r e s t" + } + }, + + { // Wervyn Anixil + {1, 6, 0, + "MERIT:\n" + " \n" + " ANIXIL? So, that's how it is. I assume you worked out a way of using the\n" + " PSI in this state. So, not happy with your allotted PSI from the Dome, you\n" + " decided to render it in this form and take it all for yourself.\n" + " What was your reasoning for doing something this rash, though?" + }, + + {0, 9, 0, + "WERVYN ANIXIL:\n" + " \n" + " All you seem to do is complain. That's what I've discovered through this,\n" + " and it's definitely part of the reason I did it. I do creative things like\n" + " this sort of 'punishment' because it lets me better understand the nature of\n" + " all the forces involved. It tells me that you, MERIT, really do not have a\n" + " clue how to relate to other PSI users, and that for the most part you're\n" + " entirely self-absorbed. It tells me that, while PSI users in general doesn't\n" + " really 'get' the ethos of the forces involved here, and that the rerendered" + }, + + {0, 9, 0, + "WERVYN ANIXIL:\n" + " \n" + " PSI, as intelligent as it is, pretty much entirely understands it, though it\n" + " was mistaken that the rendering of this form was intentional.\n" + " \n" + " Regarding that, by the way, THAT is the reason I thought you needed a\n" + " punishment. Making ridiculously inefficient, wasteful use of the limited PSI\n" + " we have available wouldn't have been a problem if it was just the normal PSI\n" + " like we'd planned it to be. But you knew full well that PSI users who wanted" + }, + + {0, 9, 0, + "WERVYN ANIXIL:\n" + " \n" + " nothing to do with it were being greatly inconvenienced by usage clogging up\n" + " the limited ley-lines we have. That makes you an asshole, a deliberate prick\n" + " to everyone in the world because you thought you could get away with it, and\n" + " that it wouldn't matter.\n" + " \n" + " I also take issue with this nonsense about your punishment being severe,\n" + " because it's not. You can still use your PSI as you used it before, to a" + }, + + {0, 9, 0, + "WERVYN ANIXIL:\n" + " \n" + " similar level of effectiveness. I specifically allowed access to THIS form\n" + " of PSI so that you couldn't complain that you couldn't use magic. Now\n" + " exiling you to the Dome had multiple meanings. One was to effectively tell\n" + " you, 'I don't think you have anything of value to do, so I'm putting you in\n" + " a place where no one does anything of value.' I still think that, by the\n" + " way. Another was, like I said, to see what you would do. Maybe you would\n" + " actually cast something worth the PSI, then. Or maybe you would take the" + }, + + {0, 8, 0, + "WERVYN ANIXIL:\n" + " \n" + " hint and just stop it for the week until I decided you'd had enough. But you\n" + " couldn't do either of those things, because all you do is complain.\n" + " \n" + " I want you to take a look at yourself and ask what you want to accomplish\n" + " here, and what you think is keeping you here. Obviously you have some\n" + " attachment, even though you keep saying the new PSI is worthless. This isn't" + }, + + {0, 9, 0, + "WERVYN ANIXIL:\n" + " \n" + " true, of course, ATARAXIA really captures a good point here. For the most\n" + " part, PSI users just find you extremely annoying because you continually set\n" + " yourself at odds with pretty much every kind of PSI usage but your own, and\n" + " then complain about it. Do you want things to be different? They certainly\n" + " can be. 'EVEN IF I DO CHANGE, THEN MY PSI WON'T BE GOOD ENOUGH!' That's\n" + " bullshit. All other PSI users seem to be on the path to figuring out how to\n" + " just use it. You can too." + }, + + {0, 9, 0, + "WERVYN ANIXIL:\n" + " \n" + " Your 'punishment' is up tomorrow, I'd only planned to do it for a week. Of\n" + " course, you can continue complaining, and I may have to think of something\n" + " else to do to you. But I'm not going to kill you. If you want to leave this\n" + " mortal coil because you can't take it anymore, then leave. If you don't want\n" + " to leave, then start working out your issues with the new PSI, I actually\n" + " would love to help. And if you don't want to do that, then I'm sorry, but\n" + " you're going to continue being useless at magic, because the way you are" + }, + + {0, 4, 0, + "WERVYN ANIXIL:\n" + " \n" + " now, it's not surprising at all that your PSI ability is as stunted as it\n" + " is." + }, + + {1, 4, 0, + "MERIT:\n" + " \n" + " The tainted PSI has driven even you mad. Mad with power. This is a blow I\n" + " I must strike, for everyone. For humanity." + }, + + {0, 3, 1, + "WERVYN ANIXIL:\n" + " \n" + " I'll meet you at the point of diminishing returns." + } + }, + + { // Wervyn Anixil???? (with Agate Knife) + {1, 8, 0, + "MERIT:\n" + " \n" + " ANIXIL? So, that's how it is. I assume you worked out a way of using the\n" + " PSI in this state. So, not happy with your allotted PSI from the Dome, you\n" + " decided to render it in this form and take it all for yourself.\n" + " What was your reasoning for doing something this rash, though?\n" + " \n" + " That was what you wanted to hear, right?" + }, + + {0, 7, 0, + "WERVYN ANIXIL????:\n" + " \n" + " All you seem to do is complain. That's what I've discovered through this,\n" + " and it's definitely part of the ... ... ... ... ... ... ... ... ... ... ...\n" + " ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...\n" + " ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...\n" + " ... ... oh." + }, + + {1, 6, 0, + "MERIT:\n" + " \n" + " If I've learned anything in life, it's that things are often not quite as\n" + " they seem. You couldn't use the Agate Knife, so you attempted to hide it.\n" + " \n" + " Unfortunately for you, I found it and recognised it." + }, + + {0, 7, 0, + "WERVYN ANIXIL????:\n" + " \n" + " . . . . no. It's not just recognising the Knife at all. You were actually\n" + " able to use it, too.\n" + " \n" + " So. You too knew the secret?" + }, + + {1, 5, 0, + "MERIT:\n" + " \n" + " I was one of the few that he told.\n" + " \n" + " And so I know." + }, + + {0, 3, 0, + "????:\n" + " \n" + " Umm..." + }, + + {1, 7, 0, + "MERIT:\n" + " \n" + " Well, it wasn't a bad attempt. If I hadn't found THAT, I would never have\n" + " known.\n" + " \n" + " Now that that's taken care of, I might as well finish what I came here for.\n" + " Prepare yourself." + }, + + {0, 3, 1, + "????:\n" + " \n" + " I won't hold back! There's not a lot of point, now." + } + } +}; + +void BossDialog() +{ + int ypos; + int lines; + int dialog_set; + + dialog_set = current_boss; + + if (current_boss == 3) { + if (player_shield == 30) { + dialog_set = 4; + } + } + + lines = dtext[dialog_set][boss_dlg-1].lines; + if (enter_pressed) { + if (dtext[dialog_set][boss_dlg-1].last) { + boss_dlg = 0; + boss_fight_mode = 2; + } else { + boss_dlg++; + } + enter_pressed = 0; + } + if (boss_dlg > 0) { + if (dtext[dialog_set][boss_dlg-1].pos == 0) { + ypos = 29; + } else { + ypos = 466 - lines * 10 - 14; + } + DrawRect(0, ypos, 640, lines * 10 + 14, 0); + DrawRect(2, ypos+2, 636, lines * 10 + 10, 40); + DrawRect(4, ypos+4, 632, lines * 10 + 6, 80); + draw_text(8, ypos+8, dtext[dialog_set][boss_dlg-1].txt, 255); + } +} + +void InitBossVars() +{ + current_boss_room = 0; + current_boss = 0; + boss_tail_len = 0; + boss_fight_mode = 0; + boss_flash = 0; + boss_new_life = 0; + final_boss_dlg = 0; +} + +void Curse() +{ + int i; + int x, y; + unsigned char tile; + struct RoomConnection *rc; + // Upon taking the cursed seal + + // Make the place of power your checkpoint + + checkpoint_x = rooms[place_of_power].w * 16 + rooms[place_of_power].x * 32; + checkpoint_y = rooms[place_of_power].h * 16 + rooms[place_of_power].y * 32; + + // Turn the start room into a boss room, and clear it + + rooms[0].room_type = 2; + Paint(rooms[0].x+1, rooms[0].y+1, rooms[0].w-2, rooms[0].h-2, "dat/d/fbossroom.loc"); + + // Lock all unvisited rooms off + + for (i = 0; i < 3000; i++) { + if (!rooms[i].visited) { + rc = rooms[i].con; + + while (rc != NULL) { + x = rc->x2; + y = rc->y2; + tile = Get(x, y); + if ((tile >= 38)&&(tile <= 41)) tile = tile - 38 + 4; + if ((tile >= 21)&&(tile <= 24)) tile = tile - 21 + 4; + if ((tile >= 13)&&(tile <= 16)) tile = tile - 13 + 4; + Put(x, y, tile, GetRoom(x, y)); + rc = rc->n; + } + rooms[i].con = NULL; + rooms[i].connections = 0; + } + } + + // Every 25th monster becomes a curse. All other monsters are deleted + + CurseEnemies(); +} + +void DrawPowerObject() +{ + int p_x, p_y; + int p_obj = rooms[player_room].room_param; + static int tick = 0; + static int collect = 0; + int i; + int required_enemies; + int n_artifacts; + int dx, dy; + int hover_v, off_v; + float ddir; + int dmag; + + if (place_of_power == player_room) p_obj = 3; + + n_artifacts = current_boss + artifacts[8] + artifacts[9] + artifacts[10]; + + required_enemies = total_enemies * (percent_required[n_artifacts]) / 100; + + if (rooms[player_room].room_type == 4) required_enemies = 0; + + SDL_Rect from, to; + + hover_v = 16; + off_v = 48; + + p_x = (rooms[player_room].w * 32 / 2 - 16) + rooms[player_room].x * 32; + + if (!game_paused) { + if ((rooms[player_room].room_type == 5) || (rooms[player_room].room_type == 6)) { + if (CanGetArtifact()) { + if ((Get((player_x+PLAYERW/2)/32, (player_y+PLAYERH/2)/32)==42) || + (PlayerDist(rooms[player_room].w * 16 + rooms[player_room].x * 32, + rooms[player_room].h * 16 + rooms[player_room].y * 32) < 32)) { + if (rooms[player_room].enemies == 0) { + off_v = 48 - (collect * 48 / 100); + hover_v = 16 - (collect * 16 / 100); + + //DrawCircle(p_x + 16 - scroll_x, player_y - 48 - scroll_y, collect * 4 + 1, rand()%192+64); + DrawCircle(player_x + PLAYERW/2 - scroll_x, player_y + PLAYERH/2- scroll_y, (100-collect) * 4 + 1, rand()%192+64); + + collect++; + if (collect == 1) { + SND_Pos("dat/a/crystal2.wav", 100, 0); + } + if (collect > 100) { + collect = 0; + rooms[player_room].room_type = 4; + artifacts[8 + p_obj] = 1; + specialmessage = 30 + p_obj; + specialmessagetimer = 120; + + if (p_obj == 3) { + Curse(); + } + } + } + } + } + } + } + + p_y = (rooms[player_room].h * 32 / 2 - 16) + rooms[player_room].y * 32 - off_v + sin((float)tick / 20.0)*hover_v; + + from.x = (8 + p_obj) * 32; + from.y = 0; + from.w = 32; + from.h = 32; + + to.x = p_x - scroll_x; + to.y = p_y - scroll_y; + + if (killed_enemies >= required_enemies) { + DrawCircle(p_x + 16 - scroll_x, p_y + 16 - scroll_y, 34+rand()%5 + collect * 4 + 1, rand()%128+128); + } + SDL_BlitSurface(artifact_spr, &from, screen, &to); + + //printf("Required: %d Killed %d Value: %d\n", required_enemies, killed_enemies, (int)(sqrt((required_enemies - killed_enemies)/4)+5)); + for (i = 0; i < (int)(sqrt((required_enemies - killed_enemies))+5); i++) { + ddir = (float)(rand()%256) * M_PI * 2 / 256; + dmag = rand()%20; + dx = p_x + 16 + cos(ddir)*dmag; + dy = p_y + 16 + sin(ddir)*dmag; + DrawCircle(dx - scroll_x, dy - scroll_y, rand()%5+1, 0); + DrawCircle(dx - scroll_x, dy - scroll_y, rand()%3+1, 64); + } + + tick++; +} + +void DrawBossHP(int bar_length) +{ + static SDL_Surface *boss_hp_icon = NULL; + int draw_amt; + Uint8 draw_col; + SDL_Rect to; + to.x = 0; + to.y = 29; + + if (boss_hp_icon == NULL) { + boss_hp_icon = IMG_Load("dat/i/boss_icon.png"); + } + SDL_BlitSurface(boss_hp_icon, NULL, screen, &to); + DrawRect(16, 28, 624, 17, 0); + DrawRect(17, 29, 622, 15, 32); + DrawRect(18, 30, 620, 13, 64); + + draw_col = 128 + (boss_hp * 127 / 1000); + if (bar_length < 100) { + draw_amt = bar_length * 614 / 100; + } else { + draw_amt = boss_hp * 614 / 1000; + } + + DrawRect(20, 32, draw_amt+2, 9, draw_col * 2 / 3); + DrawRect(21, 33, draw_amt, 7, draw_col); + + + if ( (current_boss == 3) && (player_shield == 30) ) { + draw_text(22, 33, "??????????????", 0); + } else { + draw_text(22, 33, boss_names[current_boss], 0); + } +} + +int PDir(int x1, int y1, int x2, int y2) +{ + int dx, dy; + + dx = x2 - x1; + dy = y2 - y1; + + return atan2(dy, dx); +} + +float CHDir(float original_dir, float new_dir, float dir_delta) +{ + while (original_dir < 0) original_dir += M_PI * 2; + while (new_dir < 0) new_dir += M_PI * 2; + + original_dir = fmodf(original_dir, M_PI * 2) + M_PI * 2; + new_dir = fmodf(new_dir, M_PI * 2) + M_PI * 2; + + if (fabs(original_dir - new_dir) <= dir_delta) return new_dir; + if (fabs((original_dir + M_PI * 2) - new_dir) <= dir_delta) return new_dir; + if (fabs(original_dir - (new_dir + M_PI * 2)) <= dir_delta) return new_dir; + + if (original_dir > new_dir) { + if ((original_dir - new_dir) <= M_PI) { + return original_dir - dir_delta; + } + if (((new_dir + M_PI*2) - original_dir) <= M_PI) { + return original_dir + dir_delta; + } + } else { + if ((new_dir - original_dir) <= M_PI) { + return original_dir + dir_delta; + } + if (((original_dir + M_PI*2) - new_dir) <= M_PI) { + return original_dir - dir_delta; + } + } + + exit(1); + + return original_dir; +} + +void TryHurtBoss(int x, int y, int range, int power) +{ + int atk_power; + if (boss_new_life) return; + if (dist(x, y, boss_x, boss_y) <= range) { + atk_power = 400 * power / circuit_size + power / 75; + if (atk_power > boss_breakpoint) { + boss_hp -= (atk_power - boss_breakpoint) * boss_dmgrate; + SND_Pos("dat/a/enemyhit.wav", 128, dist(x, y, boss_x, boss_y) / 4); + boss_flash = 40; + if (boss_hp <= 0) { + boss_new_life = 1; + boss_hp = 0; + } + } + } +} + +int BossMovement(int move_x, int move_y) +{ + if (!IsSolid(Get( (move_x - 12 + (current_boss==3)*6)/32, (move_y - 12 + (current_boss==3)*4)/32))) { + if (!IsSolid(Get( (move_x + 12 - (current_boss==3)*6)/32, (move_y - 12 + (current_boss==3)*4)/32))) { + if (!IsSolid(Get( (move_x - 12 + (current_boss==3)*6)/32, (move_y + 12 - (current_boss==3)*4)/32))) { + if (!IsSolid(Get( (move_x + 12 - (current_boss==3)*6)/32, (move_y + 12 - (current_boss==3)*4)/32))) { + return 1; + } + } + } + } + return 0; +} + +void DrawBoss() +{ + int flash_coeff; + static int t = 0; + t++; + + flash_coeff = (boss_flash > 0)&&((boss_flash/3) % 2) ? 255 : 0; + if (boss_flash > 0) boss_flash--; + switch (current_boss) { + case 0: { + static SDL_Surface *boss_spr = NULL; + static int tail_x[10], tail_y[10]; + int i; + + SDL_Rect drawpos; + if (boss_spr == NULL) { + boss_spr = IMG_Load("dat/i/boss1.png"); + SDL_SetColorKey(boss_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + } + + if (boss_tail_len == 0) { + tail_x[boss_tail_len] = boss_x; + tail_y[boss_tail_len] = boss_y; + boss_tail_len++; + } else { + if (dist(tail_x[0], tail_y[0], boss_x, boss_y) >= 24) { + if (boss_tail_len < 10) boss_tail_len++; + for (i = 9; i >= 0; i--) { + tail_x[i+1] = tail_x[i]; + tail_y[i+1] = tail_y[i]; + } + tail_x[0] = boss_x; + tail_y[0] = boss_y; + } else { + for (i = 0; i < boss_tail_len; i++) { + tail_x[i] += -1 + rand() % 3; + tail_y[i] += -1 + rand() % 3; + } + } + } + + for (i = 0; i < boss_tail_len; i++) { + DrawCircleEx(tail_x[i] - scroll_x, tail_y[i] - scroll_y, 48 - i * 3, 0, 96 ^ flash_coeff); + } + for (i = 0; i < boss_tail_len; i++) { + DrawCircleEx(tail_x[i] - scroll_x, tail_y[i] - scroll_y, 44 - i * 3, 0, 0 ^ flash_coeff); + } + + drawpos.x = boss_x - 16 - scroll_x; + drawpos.y = boss_y - 16 - scroll_y; + + SDL_BlitSurface(boss_spr, NULL, screen, &drawpos); + + break; + } + case 1: { + static SDL_Surface *boss_spr = NULL; + float h_dist; + float new_dst, new_dir; + int i, j; + int hx[4], hy[4]; + int heads; + int mx, my; + int check_pass; + + heads = 5 - boss_lives; + h_dist = M_PI * 2 / heads; + + SDL_Rect drawpos; + if (boss_spr == NULL) { + boss_spr = IMG_Load("dat/i/boss2.png"); + SDL_SetColorKey(boss_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + } + + new_dst = 64 + sin((float)t / 80.0) * 24; + + new_dir = boss_2h_dir; + new_dir += (float)(rand() % 16) / 128.0; + new_dir -= (float)(rand() % 16) / 128.0; + + // Check new dists + check_pass = 1; + for (i = 0; i < heads; i++) { + mx = boss_x + cos(new_dir + h_dist * i) * new_dst; + my = boss_y + sin(new_dir + h_dist * i) * new_dst; + + if (!BossMovement(mx, my)) + check_pass = 0; + } + + if (check_pass) { + boss_2h_dst = new_dst; + boss_2h_dir = new_dir; + } + + // Heads + + for (i = 0; i < heads; i++) { + hx[i] = boss_x + cos(boss_2h_dir + h_dist * i) * boss_2h_dst; + hy[i] = boss_y + sin(boss_2h_dir + h_dist * i) * boss_2h_dst; + } + + // Membranes + for (j = 0; j < heads; j++) { + for (i = 0; i < 5; i++) { + mx = hx[j] + (hx[(j+1)%heads] - hx[j]) * i / 4; + my = hy[j] + (hy[(j+1)%heads] - hy[j]) * i / 4; + + DrawCircleEx(mx - scroll_x, my - scroll_y, 24 + abs(i - 2)*12 + rand()%6, 0, 96 ^ flash_coeff); + } + } + for (j = 0; j < heads; j++) { + for (i = 0; i < 5; i++) { + mx = hx[j] + (hx[(j+1)%heads] - hx[j]) * i / 4; + my = hy[j] + (hy[(j+1)%heads] - hy[j]) * i / 4; + + DrawCircleEx(mx - scroll_x, my - scroll_y, 20 + abs(i - 2)*10 + rand()%6, 0, 0 ^ flash_coeff); + } + } + + // Draw heads + + for (i = 0; i < heads; i++) { + drawpos.x = hx[i] - 16 - scroll_x; + drawpos.y = hy[i] - 16 - scroll_y; + + SDL_BlitSurface(boss_spr, NULL, screen, &drawpos); + } + + break; + } + case 2: { + static SDL_Surface *boss_spr = NULL; + int i; + int mx, my; + float md; + SDL_Rect drawfrom, drawpos; + + if (boss_spr == NULL) { + boss_spr = IMG_Load("dat/i/boss3.png"); + SDL_SetColorKey(boss_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + } + + boss_m_heads = boss_lives; + + DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, 48, 0, 0 ^ flash_coeff); + // Draw appendages + for (i = 0; i < boss_m_heads; i++) { + mx = boss_x; + my = boss_y; + md = M_PI * 2.0 / (float)boss_m_heads * i + (M_PI / 4); + + while (dist(mx, my, boss_m_hx[i], boss_m_hy[i]) > 12) { + md = CHDir(md, PDir(mx, my, boss_m_hx[i], boss_m_hy[i]), 0.5); + //md = PDir(mx, my, boss_m_hx[i], boss_m_hy[i]); + mx += cos(md) * 12; + my += sin(md) * 12; + + DrawCircleEx(mx - scroll_x, my - scroll_y, 16 + rand()%8, 0, 0 ^ flash_coeff); + } + } + for (i = 0; i < boss_m_heads; i++) { + DrawCircleEx(boss_m_hx[i] - scroll_x, boss_m_hy[i] - scroll_y, 28, 0, 0 ^ flash_coeff); + } + for (i = 0; i < boss_m_heads; i++) { + mx = boss_x; + my = boss_y; + md = M_PI * 2.0 / (float)boss_m_heads * i + (M_PI / 4); + + while (dist(mx, my, boss_m_hx[i], boss_m_hy[i]) > 12) { + md = CHDir(md, PDir(mx, my, boss_m_hx[i], boss_m_hy[i]), 0.5); + //md = PDir(mx, my, boss_m_hx[i], boss_m_hy[i]); + mx += cos(md) * 12; + my += sin(md) * 12; + drawfrom.x = 32; + drawfrom.y = 32 * (flash_coeff > 0); + drawfrom.w = 32; + drawfrom.h = 32; + drawpos.x = mx - 16 - scroll_x; + drawpos.y = my - 16 - scroll_y; + + SDL_BlitSurface(boss_spr, &drawfrom, screen, &drawpos); + } + } + // Draw heads + drawfrom.x = 0; + drawfrom.y = 32 * (flash_coeff > 0); + drawfrom.w = 32; + drawfrom.h = 32; + for (i = 0; i < boss_m_heads; i++) { + drawpos.x = boss_m_hx[i] - 16 - scroll_x; + drawpos.y = boss_m_hy[i] - 16 - scroll_y; + SDL_BlitSurface(boss_spr, &drawfrom, screen, &drawpos); + } + + // Draw core + for (i = 0; i < boss_m_heads; i++) { + drawpos.x = boss_x - 16 - scroll_x; + drawpos.y = boss_y - 16 - scroll_y; + + SDL_BlitSurface(boss_spr, &drawfrom, screen, &drawpos); + } + break; + } + case 3: { + SDL_Rect drawto; + static SDL_Surface *boss_spr = NULL; + + if (boss_spr == NULL) { + boss_spr = IMG_Load("dat/i/boss4.png"); + SDL_SetColorKey(boss_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + } + + // Aura + + DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, 64+rand()%4, 0, (192+rand()%64) ^ flash_coeff); + DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, 48+rand()%4, 0, (rand()%64) ^ flash_coeff); + + drawto.x = boss_x - 16 - scroll_x; + drawto.y = boss_y - 16 - scroll_y; + + SDL_BlitSurface(boss_spr, NULL, screen, &drawto); + break; + } + } +} + +void BC_BossIntro() +{ + static int boss_bar_fill = 0; + static int boss_circle = 0, circle_reduce = 128; + + if (boss_dlg != 0) { + DrawBoss(); + DrawBossHP(100); + return; + } + + if (boss_bar_fill < 100) { + boss_bar_fill += 2; + DrawBossHP(boss_bar_fill); + } else { + if (boss_circle < 128) { + boss_circle += 2; + DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, boss_circle, 0, 0); + DrawCircle(boss_x - scroll_x, boss_y - scroll_y, boss_circle, 96); + } else { + if (circle_reduce > 4) { + circle_reduce -= 2; + DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, circle_reduce, 0, 0); + DrawCircle(boss_x - scroll_x, boss_y - scroll_y, circle_reduce, 96); + DrawBoss(); + } else { + DrawBoss(); + if ((final_boss_dlg == 0) && (current_boss == 3)) { + boss_dlg = 1; + final_boss_dlg = 1; + } else { + boss_fight_mode = 2; + } + boss_circle = 0; + boss_bar_fill = 0; + circle_reduce = 128; + } + } + DrawBossHP(100); + } +} + +void BC_BossCombat() +{ + static int t = 0; + int move_x, move_y; + float pdir; + float pdir_x, pdir_y; + int i, j; + t++; + + pdir = PlayerDir(boss_x, boss_y); + pdir_x = cos(pdir); + pdir_y = sin(pdir); + + move_x = boss_x; + move_y = boss_y; + DrawBoss(); + switch (current_boss) { + case 0: { + static float boss_dir = 0.0; + static float boss_dir_offset = 1.0; + + move_x += cos(boss_dir) * (4 + (3 - boss_lives)/2); + move_y += sin(boss_dir) * (4 + (3 - boss_lives)/2); + + move_x += pdir_x * (2 + (3 - boss_lives)/2); + move_y += pdir_y * (2 + (3 - boss_lives)/2); + + boss_dir += (float)(rand() % 64)/256 * boss_dir_offset; + if ((rand()%48) == 0) { + boss_dir_offset *= -1.0; + } + while (!BossMovement(move_x, move_y)) { + boss_dir += (float)(rand() % 64)/256 * boss_dir_offset; + if ((rand()%48) == 0) { + boss_dir_offset *= -1.0; + } + move_x = boss_x; + move_y = boss_y; + + move_x += cos(boss_dir) * (4 + (3 - boss_lives)/2); + move_y += sin(boss_dir) * (4 + (3 - boss_lives)/2); + move_x += pdir_x * (2 + (3 - boss_lives)/2); + move_y += pdir_y * (2 + (3 - boss_lives)/2); + } + if (BossMovement(move_x, move_y)) { + boss_x = move_x; + boss_y = move_y; + } + if (boss_flash > 15) return; + + switch (boss_lives) { + case 3: { + int laser_dmg; + float laser_1_dir, laser_2_dir; + + laser_dmg = (rand()%(player_shield/2+1))/2; + + for (i = 0; i < player_shield / 5 + 1; i++) { + SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 3, pdir + M_PI / 4, 2, 0); + SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 0, pdir, 4.5, 0); + } + laser_1_dir = pdir + 0.3 + sin((float)t / 8)*0.2; + laser_2_dir = pdir - 0.3 + sin((float)t / 8)*0.2; + SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_1_dir, 0, 1, 0, laser_dmg); + SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_2_dir, 0, 1, 0, laser_dmg); + break; + } + case 2: { + int laser_dmg; + float laser_1_dir, laser_2_dir, laser_3_dir; + + laser_dmg = (rand()%(player_shield/2+1))/2; + for (i = 0; i < player_shield / 3 + 1; i++) { + SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 3, RandomDir(), 3.5, 0); + SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 0, RandomDir(), 4.5, 0); + } + laser_1_dir = pdir + 0.4 + sin((float)t / 8)*0.2; + laser_2_dir = pdir - 0.4 + sin((float)t / 8)*0.2; + laser_3_dir = RandomDir(); + SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_1_dir, 0, 1, 0, laser_dmg); + SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_2_dir, 0, 1, 0, laser_dmg); + if ((t % 12) == 0) + SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_3_dir, 24, 8, 0, laser_dmg * 4); + break; + } + case 1: { + int laser_dmg; + float laser_1_dir, laser_2_dir, laser_3_dir; + + laser_dmg = (rand()%(player_shield/2+1))/2; + for (i = 0; i < player_shield / 4 + 1; i++) { + SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 3, RandomDir(), 3.5, 0); + SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 0, pdir, 5.0, 0); + SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 0, RandomDir(), 4.5, 0); + } + laser_1_dir = pdir + 0.4 + sin((float)t / 8)*0.3; + laser_2_dir = pdir - 0.4 + sin((float)t / 8)*0.3; + laser_3_dir = RandomDir(); + SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_1_dir, 0, 1, 0, laser_dmg); + SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_2_dir, 0, 1, 0, laser_dmg); + if ((t % 5) == 0) + SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, RandomDir(), 16, 16, 0, laser_dmg * 4); + if ((t % 10) == 0) + SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 5, RandomDir(), 5, 0); + break; + } + } + break; + } + case 1: { + float h_dist; + int i, j; + int hx[4], hy[4]; + int heads; + int mx, my; + int check_pass; + float hd2; + + heads = 5 - boss_lives; + h_dist = M_PI * 2 / heads; + + // Heads + + for (i = 0; i < heads; i++) { + hx[i] = boss_x + cos(boss_2h_dir + h_dist * i) * boss_2h_dst; + hy[i] = boss_y + sin(boss_2h_dir + h_dist * i) * boss_2h_dst; + } + + move_x += -6 + rand() % 13; + move_y += -6 + rand() % 13; + + move_x += pdir_x * (2 + (3 - boss_lives)/2); + move_y += pdir_y * (2 + (3 - boss_lives)/2); + + // Check movement + check_pass = 1; + for (i = 0; i < heads; i++) { + mx = move_x + cos(boss_2h_dir + h_dist * i) * boss_2h_dst; + my = move_y + sin(boss_2h_dir + h_dist * i) * boss_2h_dst; + + if (!BossMovement(mx, my)) + check_pass = 0; + } + + if (check_pass) { + boss_x = move_x; + boss_y = move_y; + } + + if (boss_flash > 30) return; + + // Main cannons + if ((t % (10 + boss_lives * 6)) == 1) { + i = (t / (10 + boss_lives * 6)) % heads; + for (j = 0; j < player_shield / 2 + 1; j++) { + SpawnBullet(hx[i] + (rand() % 48)*pdir_x, hy[i] + (rand() % 48)*pdir_y, 0, pdir, 7 + ((float)(rand()%16))/10.0, 0); + } + SpawnBullet(hx[i], hy[i], 4, pdir, 10, 0); + } + + // Barrage launcher + for (i = 0; i < player_shield / 4 + heads; i++) { + SpawnBullet(boss_x, boss_y, 0, RandomDir(), 3 + (float)(rand()%16)/10.0, 0); + } + + if (boss_lives == 2) { + // Splitters + if ((t % 20) == 1) { + SpawnBullet(boss_x, boss_y, 5, pdir, 5, 0); + } + } + + // Central laser cannon + + if ((t % (100 + boss_lives * 50)) == ((100 + boss_lives * 50)-1)) { + i = player_shield / 3 + 2; + hd2 = M_PI * 2.0 / (float)i; + for (j = 0; j < i; j++) { + SpawnLaser(boss_x + cos(hd2*j) * 20, boss_y + sin(hd2*j) * 20, pdir, 15 + rand()%16, 5, 0, (player_shield / 3 + 1)); + } + } + + // Star vomit + + if (boss_lives == 2) { + for (i = 0; i < heads; i++) { + for (j = 0; j < player_shield / 6 + 1; j++) { + SpawnBullet(hx[i], hy[i], 3, RandomDir(), 6, 0); + } + } + } + + // Fusion cannon + + if (boss_lives <= 2) { + if ((t % (12 + (boss_lives - 1) * 30)) == 9) { + for (i = 0; i < heads; i++) { + SpawnLaser(hx[i], hy[i], pdir, 6, 6, 0, (player_shield / 6 + 1)); + } + } + } + break; + } + case 2: { + int mx, my; + static int t2 = 0; + float md; + int trying; + + boss_m_heads = boss_lives; + + for (i = 0; i < boss_m_heads; i++) { + mx = boss_m_hx[i]; + my = boss_m_hy[i]; + + boss_m_hd[i] = CHDir(boss_m_hd[i], PlayerDir(mx, my), 0.2); + md = M_PI * 2.0 / (float)boss_m_heads * i + (M_PI / 4); + boss_m_hd[i] = CHDir(boss_m_hd[i], md, 0.1); + + md = boss_m_hd[i]; + + if ( (t % boss_m_heads) == i) { + mx += cos(md) * 4 + (boss_m_heads == 1) * 2; + my += sin(md) * 4 + (boss_m_heads == 1) * 2; + } + + + for (j = 0; j < boss_m_heads; j++) { + if (j != i) { + if (dist(boss_m_hx[j], boss_m_hy[j], boss_m_hx[i], boss_m_hy[i]) < 200) { + md = PDir(boss_m_hx[j], boss_m_hy[j], boss_m_hx[i], boss_m_hy[i]); + mx += cos(md) * 2; + my += sin(md) * 2; + boss_m_hd[i] = CHDir(boss_m_hd[i], md, 0.1); + } + } + } + + trying = 10; + + while (!BossMovement(mx, my)) { + trying--; + if (trying == 0) break; + md = PlayerDir(boss_m_hx[i], boss_m_hy[i]); + md = md - 0.3 + (float)(rand()%16) * 0.6; + + mx = boss_m_hx[i] + cos(md) * 5; + my = boss_m_hy[i] + sin(md) * 5; + + if (boss_m_heads == 1) { + boss_m_hd[i] = CHDir(boss_m_hd[i], PlayerDir(mx, my), 0.2); + md = boss_m_hd[i]; + mx += cos(md) * 3; + my += sin(md) * 3; + } + } + if (trying > 0) { + boss_m_hx[i] = mx; + boss_m_hy[i] = my; + } + } + + if (boss_flash > 30) return; + + if (boss_m_heads > 1) { + if ((t % 2) == 0) { + i = t % boss_m_heads; + SpawnBullet(boss_m_hx[i] + cos(boss_m_hd[i])*8, boss_m_hy[i] + sin(boss_m_hd[i])*8, 0, boss_m_hd[i], 10, 0); + } else { + i = (t+2) % boss_m_heads; + md = PlayerDir(boss_m_hx[i], boss_m_hy[i]); + SpawnBullet(boss_m_hx[i] + cos(boss_m_hd[i])*8, boss_m_hy[i] + sin(boss_m_hd[i])*8, 0, md, 10, 0); + } + } else { + md = CHDir(RandomDir(), PlayerDir(boss_m_hx[0], boss_m_hy[0]), 1.5); + SpawnBullet(boss_m_hx[0] + cos(boss_m_hd[0])*8, boss_m_hy[0] + sin(boss_m_hd[0])*8, 0, md, 10, 0); + } + // Barrage + + for (i = 0; i < 24; i++) { + if ((t2 % (6 - (player_shield / 6) + boss_m_heads)) == 0) { + md = RandomDir(); + if (boss_m_heads > 2) { + SpawnBullet(boss_x + cos(md)*8, boss_y + sin(md)*8, 0, md, 5, 0); + } else { + if (boss_m_heads > 1) { + SpawnBullet(boss_x + cos(md)*24, boss_y + sin(md)*24, 4, md, 12, 0); + } else { + md = CHDir(md, pdir, 0.66); + SpawnBullet(boss_x + cos(md)*24, boss_y + sin(md)*24, 4, md, 12, 0); + } + } + } + t2++; + } + + // Beams + if (boss_m_heads < 4) { + if ((t % 5) == 0) { + i = (t / 5)%boss_m_heads; + md = boss_m_hd[i]; + SpawnLaser(boss_m_hx[i], boss_m_hy[i], md, 1, 10, 0.05, (player_shield / 7) + 3 - boss_m_heads); + md += M_PI / 2; + SpawnLaser(boss_m_hx[i], boss_m_hy[i], md, 1, 10, 0.05, (player_shield / 7) + 3 - boss_m_heads); + md += M_PI / 2; + SpawnLaser(boss_m_hx[i], boss_m_hy[i], md, 1, 10, 0.05, (player_shield / 7) + 3 - boss_m_heads); + md += M_PI / 2; + SpawnLaser(boss_m_hx[i], boss_m_hy[i], md, 1, 10, 0.05, (player_shield / 7) + 3 - boss_m_heads); + } + } + + if (boss_m_heads == 1) { + if ( (t % 30) < player_shield) { + md = CHDir(RandomDir(), pdir, 1.0); + SpawnBullet(boss_m_hx[0] + cos(md)*24, boss_m_hy[0] + sin(md)*24, 3, md, 11, 0); + } + } + break; + } + case 3: + { + int boss_loop; + int boss_loop_total; + int mx, my; + int npattern; + int i; + float firedir; + float flp; + float cboss_dir; + int ls_x, ls_y; + + + mx = boss_x + cos(boss_dir)*(4 + (player_shield==30)); + my = boss_y + sin(boss_dir)*(4 + (player_shield==30)); + + if (BossMovement(mx, my)) { + boss_x = mx; + boss_y = my; + boss_dir = CHDir(boss_dir, pdir, 0.3); + } else { + boss_dir = CHDir(boss_dir, RandomDir(), 1); + } + + if ((boss_lives == 1) && (player_shield == 30)) { + { + int room_w, room_h; + int room_x, room_y; + float tmr_t; + float b_m_dir; + + float boss_x_bias, boss_y_bias; + + boss_x_bias = 1.33 - ((float)(boss_hp) / 1200.0); + boss_y_bias = 0.7 + ((float)(boss_hp) / 1500.0); + + room_x = rooms[player_room].x * 32 + 64; + room_y = rooms[player_room].y * 32 + 64; + room_w = rooms[player_room].w * 32 - 128; + room_h = rooms[player_room].h * 32 - 128; + + tmr_t = (float)t / 30.0; + + mx = (int)((sin(tmr_t * boss_x_bias)*0.5+0.5) * (float)room_w) + room_x; + my = (int)((cos(tmr_t * boss_y_bias)*0.5+0.5) * (float)room_h) + room_y; + + if ( dist(mx, my, boss_x, boss_y) < 24) { + boss_x = mx; + boss_y = my; + } else { + b_m_dir = PDir(boss_x, boss_y, mx, my); + boss_x += cos(b_m_dir)*24; + boss_y += sin(b_m_dir)*24; + } + } + if ((t % 50) < 49) { + proxy_seek = 0; + for (i = 0; i < 20; i++) { + firedir = RandomDir(); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 1, 0); + } + } else { + proxy_seek = 1; + } + if ( ((t / 50) % 4) == 3) { + proxy_seek = 1; + } + + for (i = 0; i < 24; i++) { + firedir = M_PI / 15 * (float)i + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 10, 0); + } + + break; + } + + boss_loop_total = 1 + (player_shield == 30)*2; + + cboss_dir = boss_dir; + + for (boss_loop = 0; boss_loop < boss_loop_total; boss_loop++) { + if (player_shield == 30) { + npattern = (3 - boss_lives) * 3 + 2 - (boss_hp / 334); + } else { + npattern = (2 - boss_lives) * 3 + 2 - (boss_hp / 334); + } + + switch (npattern) { + case 0: + // Spirally pattern + for (i = 0; i < 4; i++) { + firedir = M_PI / 2 * (float)i + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 5, 0); + } + for (i = 0; i < 4; i++) { + firedir = M_PI / 2 * (float)i + (float)t / 33.0 * M_PI * 2.0; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 8, 0); + } + for (i = 0; i < 4; i++) { + firedir = RandomDir(); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 12, 0); + } + break; + case 1: + // Proxies + if ((t % 100) < 80) { + for (i = 0; i < 6; i++) { + firedir = RandomDir(); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 1, 0); + } + } else { + if ((t % 100) == 88) { + proxy_seek = 1; + } + if ((t % 100) == 92) { + proxy_seek = 0; + } + } + for (i = 0; i < 8; i++) { + firedir = M_PI / 4 * (float)i + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 10, 0); + } + break; + case 2: + // Laserwalls + if ((t % 4) == 2) { + firedir = cboss_dir + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 5, 1); + } + + for (i = 0; i < 4; i++) { + firedir = cboss_dir + M_PI / 6 * (float)i + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 5, 0); + } + break; + case 3: + if ((t % 30) == 29) { + firedir = RandomDir(); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.99, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.98, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.97, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.96, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.95, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.94, 1); + } + if ((t % 40) == 39) { + firedir = RandomDir(); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 1, firedir, 6, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 5, firedir+1, 6, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 6, firedir+1, 6, 1); + } + if ((t % 50) == 49) { + proxy_seek = 0; + firedir = RandomDir(); + for (i = 0; i < 60; i++) { + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 1, 1); + firedir += M_PI * 2.0 * (float)firedir / 60.0; + } + } + if ((t % 50) == 45) { + proxy_seek = 1; + } + + if ((t % 10) == 2) { + firedir = cboss_dir + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 7.5, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 5, 1); + } + break; + case 4: + if ((t % 80) == 79) { + firedir = RandomDir(); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.98, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.96, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.94, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.92, 1); + } + if ((t % 90) == 89) { + firedir = RandomDir(); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 1, firedir, 6, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 5, firedir+1, 6, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 6, firedir+1, 6, 1); + } + if ((t % 100) == 99) { + proxy_seek = 0; + firedir = RandomDir(); + for (i = 0; i < 60; i++) { + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 1, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 2, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 3, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 4, 1); + firedir += M_PI * 2.0 * (float)firedir / 60.0; + } + } + if ((t % 100) == 95) { + proxy_seek = 1; + } + + flp = (float)(t % 50) / 49.0; + + if ((t % 10) == 3) { + i = (t / 10) % 4; + switch (i) { + case 0: + ls_x = 7904 + flp * 576; + ls_y = 8000; + break; + case 1: + ls_x = 8480 - flp * 576; + ls_y = 8416; + break; + case 2: + ls_x = 7904; + ls_y = 8000 + flp * 416; + break; + case 3: + default: + ls_x = 8480; + ls_y = 8416 - flp * 416; + break; + } + firedir = PlayerDir(ls_x, ls_y) - 0.1 + ((float)(rand()%16))/8.0; + SpawnLaser(ls_x, ls_y, firedir, 8, 2, 0, player_shield/2+1); + } + + if ((t % 6) == 2) { + firedir = cboss_dir + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1); + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 5, 1); + } + break; + case 5: + flp = (float)(t % 50) / 49.0; + + if ((t % 8) == 7) { + i = (t / 8) % 4; + switch (i) { + case 0: + ls_x = 7904 + flp * 576; + ls_y = 8000; + break; + case 1: + ls_x = 8480 - flp * 576; + ls_y = 8416; + break; + case 2: + ls_x = 7904; + ls_y = 8000 + flp * 416; + break; + case 3: + default: + ls_x = 8480; + ls_y = 8416 - flp * 416; + break; + } + firedir = PlayerDir(ls_x, ls_y) - 0.1 + ((float)(rand()%16))/8.0; + SpawnLaser(ls_x, ls_y, firedir - 0.03*6, 5, 5, 0.03, player_shield/3+1); + } + + for (i = 0; i < 7; i++) { + firedir = cboss_dir + M_PI/2 * (float)i + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 8, 0); + } + for (i = 0; i < 7; i++) { + firedir = cboss_dir + M_PI/4 + M_PI/2 * (float)i + (float)t / 33.0 * M_PI; + SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 3, firedir, 8, 0); + } + break; + } + + cboss_dir += -0.1 + ((float)(rand()%16))/8.0; + } + break; + } + } +} + +void BC_BossDying() +{ + int x, y, rx, ry, rt; + static int t_timer = 0; + static int bxp, bdef; + int i; + static SDL_Surface *endpics[1] = {NULL}; + static float dr = 0; + + if (current_boss < 3) { + specialmessage = 40 + rooms[player_room].room_param; + specialmessagetimer = 120; + rooms[player_room].room_type = 4; + boss_fight_mode = 0; + current_boss += 1; + artifacts[8 + rooms[player_room].room_param] = 0; + + CullEnemies(4); + + // unlock doors + + for (y = 0; y < rooms[player_room].h; y++) { + for (x = 0; x < rooms[player_room].w; x++) { + rx = x + rooms[player_room].x; + ry = y + rooms[player_room].y; + rt = Get(rx, ry); + + if ((rt >= 21) && (rt <= 24)) { + Put(rx, ry, rt - 21 + 13, player_room); + + if (rt == 21) { + Put(rx, ry+1, 14, GetRoom(rx, ry+1)); + } + if (rt == 22) { + Put(rx, ry-1, 13, GetRoom(rx, ry-1)); + } + if (rt == 23) { + Put(rx+1, ry, 16, GetRoom(rx+1, ry)); + } + if (rt == 24) { + Put(rx-1, ry, 15, GetRoom(rx-1, ry)); + } + } + } + } + + if (current_boss > 0) { + SoupUpEnemies(); + } + } else { + if (boss_fight_mode == 3) { + t_timer = 0; + boss_fight_mode = 4; + magic_circuit = 0; + bxp = 4; + bdef = 256; + dr = RandomDir(); + + if (training) t_timer = 2000; + } else { + DrawArtifactOverhead(3); + if (bdef > 0) { + DrawCircleEx(boss_x-scroll_x, boss_y-scroll_y, bxp, bxp-bdef, 0); + bxp += 4; + bdef -= 2; + } else { + if (t_timer < 220) { + draw_text(244, 100, "*** Divine Seal ***", 1); + draw_text(244, 380, "*** Divine Seal ***", 1); + magic_circuit = circuit_size * 0.75 * t_timer / 220; + for (i = 0; i < 10; i++) { + rt = (rand() % 350) + 50; + DrawCircle(player_x + PLAYERW/2 - scroll_x + cos(dr)*rt, player_y - scroll_y + PLAYERH/2 + + sin(dr)*rt, rand()%30+5, rand()%128+128); + } + + dr -= 0.025; + for (i = 0; i < 5; i++) { + Arc(screen, player_x + PLAYERW/2 - scroll_x, player_y + PLAYERH/2 - scroll_y, 450, dr); + dr += 0.01; + } + dr += 0.03; + + if ((t_timer % 30) == 29) { + dr = RandomDir(); + } + + DrawCircle(player_x - scroll_x + PLAYERW/2, player_y - scroll_y + PLAYERH/2, (t_timer % 30) * 15, 255); + DrawCircle(player_x - scroll_x + PLAYERW/2, player_y - scroll_y + PLAYERH/2, ( (t_timer + 15) % 30) * 15, 255); + } else { + magic_circuit = 0; + if (boss_fight_mode < 23) { + if ( (t_timer % 4)==3) { + boss_fight_mode++; + } + } else { + if (endpics[0] == NULL) { + if (training) { + endpics[0] = IMG_Load("dat/i/wuss_ending.png"); + } + } + + if (training) { + SDL_BlitSurface(endpics[0], NULL, screen, NULL); + } else { + show_ending = 1; + } + } + } + + if (t_timer == 200) { + CullEnemies(1); + } + + t_timer++; + } + } + } +} + +void BC_NewLife() +{ + static int circle_size = 0; + static int circle_size2 = 128; + + if (boss_flash > 0) { + DrawBoss(); + return; + } + if (boss_new_life == 1) { + circle_size = 0; + circle_size2 = 128; + boss_ox = rooms[current_boss_room].w * 16 + rooms[current_boss_room].x * 32; + boss_oy = rooms[current_boss_room].h * 16 + rooms[current_boss_room].y * 32; + boss_new_life = 2; + } else { + if (circle_size < 128) { + circle_size += 4; + DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, circle_size, 0, 0); + DrawCircle(boss_x - scroll_x, boss_y - scroll_y, circle_size, 96); + DrawBoss(); + + if (boss_lives > 1) { + DrawCircleEx(boss_ox - scroll_x, boss_oy - scroll_y, circle_size, 0, 0); + DrawCircle(boss_ox - scroll_x, boss_oy - scroll_y, circle_size, 96); + } + } else { + if (circle_size2 == 128) { + boss_ox = boss_x; + boss_oy = boss_y; + boss_x = rooms[current_boss_room].w * 16 + rooms[current_boss_room].x * 32; + boss_y = rooms[current_boss_room].h * 16 + rooms[current_boss_room].y * 32; + boss_tail_len = 0; + boss_lives--; + } + if (circle_size2 > 4) { + circle_size2 -= 4; + if (boss_lives > 0) { + DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, circle_size2, 0, 0); + DrawCircle(boss_x - scroll_x, boss_y - scroll_y, circle_size2, 96); + DrawBoss(); + } + + DrawCircleEx(boss_ox - scroll_x, boss_oy - scroll_y, circle_size2, 0, 0); + DrawCircle(boss_ox - scroll_x, boss_oy - scroll_y, circle_size2, 96); + } else { + if (boss_lives > 0) { + boss_new_life = 0; + boss_hp = 1000; + if ( (boss_lives == 1) && (current_boss == 3) && (player_shield == 30) ) { + player_hp = 6; + boss_dmgrate = 0.2; + } + } else { + boss_new_life = 0; + boss_fight_mode = 3; + } + } + } + } +} + +void BossControl() +{ + if ((player_room != current_boss_room)) { + // Player left, so roll back the boss + resetboss = 0; + current_boss_room = 0; + boss_tail_len = 0; + boss_fight_mode = 0; + boss_new_life = 0; + } + + if (boss_fight_mode == 1) { + BC_BossIntro(); + return; + } + if (boss_fight_mode == 2) { + if (boss_new_life > 0) { + BC_NewLife(); + } else { + BC_BossCombat(); + } + return; + } + if (boss_fight_mode >= 3) { + BC_BossDying(); + return; + } +} + +void DrawArtifactOverhead(int p_obj) +{ + int p_x, p_y; + static int tick = 0; + + SDL_Rect from, to; + + p_x = player_x - 8; + p_y = player_y - 36 + sin((float)tick / 20.0)*4; + + from.x = (8 + p_obj) * 32; + from.y = 0; + from.w = 32; + from.h = 32; + + to.x = p_x - scroll_x; + to.y = p_y - scroll_y; + SDL_BlitSurface(artifact_spr, &from, screen, &to); + + + tick++; +} + +int CanGetArtifact() +{ + int required_enemies; + int n_artifacts; + n_artifacts = current_boss + artifacts[8] + artifacts[9] + artifacts[10]; + required_enemies = total_enemies * (percent_required[n_artifacts]) / 100; + + if (killed_enemies >= required_enemies) return 1; + return 0; +} + +void BossRoom(int room) +{ + int i; + + boss_fight_mode = 1; + current_boss_room = room; + boss_x = rooms[room].w * 16 + rooms[room].x * 32; + boss_y = rooms[room].h * 16 + rooms[room].y * 32; + boss_hp = 1000; + boss_flash = 0; + magic_circuit = 0; + boss_dlg = 0; + boss_new_life = 0; + + switch (current_boss) { + case 0: + boss_lives = 3; + boss_breakpoint = 75; + boss_dmgrate = 1.00; + break; + case 1: + boss_lives = 3; + boss_breakpoint = 75; + boss_dmgrate = 0.80; + break; + case 2: + boss_lives = 4; + boss_breakpoint = 90; + boss_dmgrate = 1.25; + boss_m_heads = 4; + for (i = 0; i < boss_m_heads; i++) { + boss_m_hd[i] = M_PI / 2 * i + (M_PI / 4); + boss_m_hx[i] = boss_x + cos(boss_m_hd[i]) * 128; + boss_m_hy[i] = boss_y + sin(boss_m_hd[i]) * 128; + } + break; + case 3: + boss_lives = 2 + (player_shield == 30); + boss_breakpoint = 120; + boss_dmgrate = 0.40 + 0.10*(player_shield == 30); + boss_dir = M_PI * 3.0 / 2.0; + break; + } + + if (training) { + boss_dmgrate *= 1.2; + boss_breakpoint *= 0.8; + } +} diff --git a/src/boss.h b/src/boss.h new file mode 100755 index 0000000..3c0a82e --- /dev/null +++ b/src/boss.h @@ -0,0 +1,56 @@ +// +// boss.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// Exposes boss.c functionality and types + +#ifndef BOSS_H +#define BOSS_H + +extern int boss_fight_mode, current_boss_room; +extern char *boss_names[]; +extern char *artifact_names[]; +extern int current_boss; +extern int boss_lives; + +void DrawPowerObject(); +void DrawArtifactOverhead(int p_obj); +int CanGetArtifact(); + +void BossRoom(int room); +void BossControl(); + +void TryHurtBoss(int x, int y, int range, int power); + +void InitBossVars(); + +void DrawBossHP(int bar_length); + +int PDist(int x1, int y1, int x2, int y2); + +extern int proxy_seek; + +extern int boss_dlg; + +extern int resetboss; + +void BossDialog(); + +#endif diff --git a/src/demon.c b/src/demon.c new file mode 100755 index 0000000..e69ea70 --- /dev/null +++ b/src/demon.c @@ -0,0 +1,2613 @@ +// +// demon.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include +#include + +#include "levelblit.h" +#include "mapgen.h" +#include "save.h" +#include "audio.h" +#include "boss.h" +#include "tiles.h" + +SDL_Surface *reticle; +SDL_Surface *inrange; +SDL_Surface *invis_enemy; + +int searched[3000]; +int searchdist[3000]; +int csearch; +int room_active[3000]; + +int fc_open = 0; + +int max_activate_dist = 0; + +// enemy + +int sqr(int x) +{ + return x*x; +} + +struct enemy { + int x, y; + int room; + int lives; + int deaths; + int str; + int curr_follow; + + int followdepth; + + int enemy_type; + + SDL_Surface *image; + int blit_pos; + + int creationcost; + + int teleport_v; + int speed; + int t; + int active; + int fire_rate; + + int last_room; + int p_last_room; + + float move_dir; + + int min_gems; + int max_gems; + + struct enemy *next; + struct enemy *next_active; + + int delete_me; + int dying; + + struct RoomConnection *m_exit; +}; + +// shot + +struct bullet { + float x, y; + struct enemy *firer; + struct bullet *parent; + int room; + float speed; + float dir; + int img; + // img: 0 = bullet + // 1 = star + // 2 = laser + // 3 = ministar + // 4 = rocket + // 5 = replishot + // 6 = laserstar + // 7 = proxy + // 8 = remote laser + int t; + + int invuln; + float natural_dir; + + int fire_time; + int duration; + float turn; + + int shield_damage; + + struct bullet *next; + + int dying; + int delete_me; +}; + +struct diamond { + int x, y; + int room; + int t; + struct diamond *next; + struct diamond *next_in_room; + struct diamond *prv_in_room; + int value; + int delete_me; +}; + +struct enemyloc { + struct enemy *e; + struct enemyloc *n; +}; + +int total_enemies = 0; +int killed_enemies = 0; +int total_bullets = 0; +int active_enemies = 0; +int total_gems = 0; + +struct enemy *enemy_stack = NULL; +struct bullet *bullet_stack = NULL; +struct enemy *active_stack = NULL; +struct diamond *gem_stack = NULL; + +struct diamond *room_gems[3000] = {NULL}; + +struct enemyloc *enemy_loc_stack[20][20] = {{NULL}}; + +struct enemy *CreateEnemy(int enemy_x, int enemy_y, int enemy_room); +struct enemy *CreateEnemyEx(int enemy_x, int enemy_y, int enemy_room, int enemy_type); +void SCreateGem(int x, int y, int r, int v); +void ActivateEnemies(int room); +void SoupUpEnemies(); + +void SpawnLaser(int x, int y, float dir, int fire_time, int duration, float turn, int dmg); + +void DestroyThings() +{ + struct enemy *ec, *ed; + struct bullet *bc, *bd; + struct diamond *dc, *dd; + int i; + + ec = enemy_stack; + bc = bullet_stack; + dc = gem_stack; + + while (ec) { + ed = ec; + ec = ec->next; + free(ed); + } + while (bc) { + bd = bc; + bc = bc->next; + free(bd); + } + while (dc) { + dd = dc; + dc = dc->next; + free(dd); + } + + enemy_stack = NULL; + bullet_stack = NULL; + gem_stack = NULL; + active_stack = NULL; + + for (i = 0; i < 3000; i++) { + room_gems[i] = NULL; + } +} + +struct GRD_Box { + int d[60][60]; +}; +int GetRoomDist(int room1, int room2); +int FindRoomDist(int room1, int room2) +{ + struct RoomConnection *follow; + int mdist = 1000000; + int cdist; + + if (room1 == room2) return 0; + fc_open++; + + searched[room1] = csearch; + + follow = rooms[room1].con; + while (follow != NULL) { + if (searched[follow->c] != csearch) { + cdist = GetRoomDist(follow->c, room2); + + if (cdist < mdist) { + mdist = cdist; + } + } + follow = follow->n; + } + fc_open--; + return mdist; +} + +int GetRoomDist(int room1, int room2) +{ + int gx, gy, dx, dy; + int temp; + int ix, iy; + static struct GRD_Box *g[50][50] = {{NULL}}; + + fc_open++; + + if (room2 < room1) { + temp = room2; + room2 = room1; + room1 = temp; + } + + gx = room1 / 60; + dx = room1 % 60; + gy = room2 / 60; + dy = room2 % 60; + + if (g[gx][gy] == NULL) { + g[gx][gy] = malloc(sizeof(struct GRD_Box)); + for (iy = 0; iy < 60; iy++) { + for (ix = 0; ix < 60; ix++) { + g[gx][gy]->d[ix][iy] = -1; + } + } + } + + if (g[gx][gy]->d[dx][dy] == -1) { + g[gx][gy]->d[dx][dy] = FindRoomDist(room1, room2); + } + fc_open--; + return g[gx][gy]->d[dx][dy]; +} + +void AddEnemyLoc(struct enemy *e, int x, int y) +{ + struct enemyloc *next; + x+=1; + y+=1; + next = enemy_loc_stack[x][y]; + enemy_loc_stack[x][y] = malloc(sizeof(struct enemyloc)); + enemy_loc_stack[x][y]->n = next; + enemy_loc_stack[x][y]->e = e; +} + +struct enemyloc *GetEnemyLoc(int s_x, int s_y) +{ + return enemy_loc_stack[s_x / 1100 + 1][s_y / 1100 + 1]; +} + +void AddEnemyPos(struct enemy *e) +{ + int x_loc, y_loc, ix, iy; + x_loc = (e->x) / 1100; + y_loc = (e->y) / 1100; + AddEnemyLoc(e, x_loc, y_loc); + ix = ((e->x - screen->w) / 1100); + iy = ((e->y - screen->h) / 1100); + if (x_loc != ix) { + AddEnemyLoc(e, ix, y_loc); + } + if (y_loc != iy) { + AddEnemyLoc(e, x_loc, iy); + } + if ((x_loc != ix) && (y_loc != iy)) { + AddEnemyLoc(e, ix, iy); + } +} + +void WriteEnemyData() +{ + struct enemy *ptr; + int n = 0; + int i = 0; + + ptr = enemy_stack; + while (ptr != NULL) { + if (!ptr->delete_me) n++; + ptr = ptr->next; + } + FWInt(n); + ptr = enemy_stack; + while (ptr != NULL) { + if (!ptr->delete_me) { + FWInt(ptr->x); + FWInt(ptr->y); + FWInt(ptr->room); + FWInt(ptr->enemy_type); + i++; + if (i % 100 == 99) { + SavingScreen(2, (float)i / (float)n); + } + } + ptr = ptr->next; + } +} + +void WriteGemData() +{ + struct diamond *ptr; + int n = 0; + int i = 0; + + ptr = gem_stack; + while (ptr != NULL) { + if (!ptr->delete_me) n++; + ptr = ptr->next; + } + FWInt(n); + ptr = gem_stack; + while (ptr != NULL) { + if (!ptr->delete_me) { + FWInt(ptr->x); + FWInt(ptr->y); + FWInt(ptr->room); + FWInt(ptr->value); + + i++; + if (i % 100 == 99) { + SavingScreen(3, (float)i / (float)n); + } + } + ptr = ptr->next; + } +} + +void WriteCreatureData() +{ + FWInt(total_enemies); + FWInt(killed_enemies); + + WriteEnemyData(); + WriteGemData(); +} + +void ReadEnemyData() +{ + int i, n; + int x, y, room, t; + + n = FRInt(); + + for (i = 0; i < n; i++) { + x = FRInt(); + y = FRInt(); + room = FRInt(); + t = FRInt(); + CreateEnemyEx(x, y, room, t); + total_enemies--; + + if (i % 100 == 99) { + LoadingScreen(2, (float)i / (float)n); + } + } + LoadingScreen(2, 1); +} + +void ReadGemData() +{ + int i, n; + int x, y, room, value; + + n = FRInt(); + + for (i = 0; i < n; i++) { + x = FRInt(); + y = FRInt(); + room = FRInt(); + value = FRInt(); + SCreateGem(x, y, room, value); + + if (i % 100 == 99) { + LoadingScreen(3, (float)i / (float)n); + } + } + LoadingScreen(3, 1); +} + +void ActivateVisited() +{ + int i; + + + for (i = 0; i < 3000; i++) { + if (rooms[i].visited) { + ActivateEnemies(i); + } + if (i % 10 == 9) { + LoadingScreen(4, (float)i / 3000.0); + } + } + LoadingScreen(4, 1); +} + +void ReadCreatureData() +{ + total_enemies = FRInt(); + killed_enemies = FRInt(); + + ReadEnemyData(); + ReadGemData(); + ActivateVisited(); +} + +SDL_Surface *enemy_sprites[10]; + +struct enemy * AllocateEnemy() +{ + return malloc(sizeof(struct enemy)); +} + +struct bullet * AllocateBullet() +{ + return malloc(sizeof(struct bullet)); +} + +struct diamond * AllocateGem() +{ + return malloc(sizeof(struct diamond)); +} + +struct enemy *CreateEnemy(int enemy_x, int enemy_y, int enemy_room) +{ + int enemy_type; + + enemy_type = rand() % (rooms[enemy_room].s_dist / 5 + 1); + if (rooms[enemy_room].room_type == 5) enemy_type += rand()%3; + if (enemy_type > 8) enemy_type = rand()%3+6; + + if (rooms[enemy_room].s_dist >= 15) { + if (rand()%64 == 0) { + enemy_type = 9; + } + } + + + return CreateEnemyEx(enemy_x, enemy_y, enemy_room, enemy_type); +} + +struct enemy *CreateEnemyEx(int enemy_x, int enemy_y, int enemy_room, int enemy_type) +{ + struct enemy *new_enemy; + + new_enemy = AllocateEnemy(); + new_enemy->x = enemy_x; + new_enemy->y = enemy_y; + new_enemy->room = enemy_room; + + rooms[enemy_room].enemies++; + new_enemy->deaths = 0; + new_enemy->t = rand() % 65536; + new_enemy->active = 0; + new_enemy->curr_follow = -1; + new_enemy->teleport_v = 0; + + new_enemy->m_exit = NULL; + + new_enemy->dying = 0; + new_enemy->delete_me = 0; + + new_enemy->last_room = -1; + new_enemy->p_last_room = -1; + + new_enemy->enemy_type = enemy_type; + + new_enemy->followdepth = 4; + + switch (enemy_type) { + case 0: + new_enemy->image = enemy_sprites[0]; + new_enemy->lives = 1; + new_enemy->str = 20; + new_enemy->speed = 3; + new_enemy->fire_rate = 20; + new_enemy->min_gems = 0; + new_enemy->max_gems = 3; + new_enemy->creationcost = 1; + break; + case 1: + new_enemy->image = enemy_sprites[1]; + new_enemy->lives = 1; + new_enemy->str = 50; + new_enemy->speed = 4; + new_enemy->fire_rate = 25; + new_enemy->min_gems = 2; + new_enemy->max_gems = 6; + new_enemy->creationcost = 1; + break; + case 2: + new_enemy->image = enemy_sprites[2]; + new_enemy->lives = 1; + new_enemy->str = 180; + new_enemy->speed = 5; + new_enemy->fire_rate = 40; + new_enemy->min_gems = 8; + new_enemy->max_gems = 15; + new_enemy->creationcost = 1; + break; + case 3: + new_enemy->image = enemy_sprites[3]; + new_enemy->lives = 2; + new_enemy->str = 220; + new_enemy->speed = 3; + new_enemy->fire_rate = 24; + new_enemy->min_gems = 12; + new_enemy->max_gems = 20; + new_enemy->creationcost = 1; + break; + case 4: + new_enemy->image = enemy_sprites[4]; + new_enemy->lives = 1; + new_enemy->str = 360; + new_enemy->speed = 3; + new_enemy->fire_rate = 32; + new_enemy->min_gems = 18; + new_enemy->max_gems = 32; + new_enemy->creationcost = 2; + break; + case 5: + new_enemy->image = enemy_sprites[5]; + new_enemy->lives = 1; + new_enemy->str = 450; + new_enemy->speed = 3; + new_enemy->fire_rate = 2; + new_enemy->min_gems = 32; + new_enemy->max_gems = 64; + new_enemy->creationcost = 2; + break; + case 6: + new_enemy->image = enemy_sprites[6]; + new_enemy->lives = 2; + new_enemy->str = 450; + new_enemy->speed = 4; + new_enemy->fire_rate = 10; + new_enemy->min_gems = 50; + new_enemy->max_gems = 100; + new_enemy->creationcost = 2; + break; + case 7: + new_enemy->image = enemy_sprites[7]; + new_enemy->lives = 1; + new_enemy->str = 500; + new_enemy->speed = 4; + new_enemy->fire_rate = 27; + new_enemy->min_gems = 80; + new_enemy->max_gems = 160; + new_enemy->creationcost = 3; + break; + case 8: + new_enemy->image = enemy_sprites[8]; + new_enemy->lives = 4; + new_enemy->str = 500; + new_enemy->speed = 2; + new_enemy->fire_rate = 8; + new_enemy->min_gems = 200; + new_enemy->max_gems = 400; + new_enemy->creationcost = 4; + break; + case 9: + new_enemy->image = enemy_sprites[0]; + new_enemy->lives = 1; + new_enemy->str = rooms[enemy_room].s_dist * 20; + new_enemy->speed = 3; + new_enemy->fire_rate = 21; + new_enemy->min_gems = 300; + new_enemy->max_gems = 600; + new_enemy->creationcost = 3; + break; + case 10: + new_enemy->image = enemy_sprites[9]; + new_enemy->lives = 8; + new_enemy->str = 500; + new_enemy->speed = 1; + new_enemy->fire_rate = 4; + new_enemy->min_gems = 5000; + new_enemy->max_gems = 6000; + new_enemy->followdepth = 8; + new_enemy->creationcost = 6; + break; + } + + if (training) { + new_enemy->str = new_enemy->str * 4 / 5; + new_enemy->fire_rate += (new_enemy->fire_rate / 2); + new_enemy->speed *= 2; + } + + if (rooms[new_enemy->room].room_type == 5) { + new_enemy->str = new_enemy->str * 3 / 2; + if (new_enemy->str > 1500) new_enemy->str = 1500; + new_enemy->fire_rate = new_enemy->fire_rate - 1; + } + + new_enemy->blit_pos = (rand()%(new_enemy->image->w / (new_enemy->image->h/new_enemy->lives)))*(new_enemy->image->h/new_enemy->lives); + new_enemy->next_active = NULL; + + new_enemy->next = enemy_stack; + enemy_stack = new_enemy; + + AddEnemyPos(new_enemy); + + total_enemies++; + + return new_enemy; +} + +void SCreateGem(int x, int y, int r, int v) +{ + struct diamond *new_gem; + + if (TileData[Get(x / 32, y / 32)].Is_Solid) { + return; + } + if (GetRoom(x / 32, y / 32) != r) { + return; + } + if (v == 0) { + return; + } + + new_gem = AllocateGem(); + + new_gem->x = x; + new_gem->y = y; + new_gem->room = r; +new_gem->delete_me = 0; + new_gem->t = rand()%65536; + new_gem->next = gem_stack; + new_gem->next_in_room = room_gems[r]; + + if (room_gems[r] != NULL) { + room_gems[r]->prv_in_room = new_gem; + } + + new_gem->value = v; + new_gem->prv_in_room = NULL; + gem_stack = new_gem; + room_gems[r] = new_gem; + + total_gems++; +} + +void CreateGem(int x, int y, int r, int v) +{ + if (v == 0) return; + if ( (rand()%1000) < ((int)log(v)/4 + (player_hp == 1)*5 + 2) ) { + SCreateGem(x, y, r, 31337); + } else { + SCreateGem(x, y, r, v); + } +} + +float PlayerDir(int x, int y) +{ + float dy = player_y+12 - y; + float dx = player_x+8 - x; + return atan2(dy, dx); +} + +int PlayerDist(int x, int y) +{ + int d = sqrt(sqr(x-(player_x+8))+sqr(y-(player_y+12))); + return d; +} + +struct bullet *CreateBullet(int x, int y, struct enemy *firer, int bullet_type, float dir, float spd) +{ + struct bullet *new_shot; + + new_shot = AllocateBullet(); + new_shot->x = x; + new_shot->y = y; + new_shot->firer = firer; + if (firer != NULL) { + new_shot->room = firer->room; + } else { + new_shot->room = GetRoom(x / 32, y / 32); + } + new_shot->dying = 0; + new_shot->delete_me = 0; + new_shot->t = rand() % 65536; + new_shot->dir = dir; + new_shot->speed = spd; + new_shot->invuln = 0; + new_shot->parent = NULL; + + switch (bullet_type) { + case 0: + new_shot->img = 0; + break; + case 1: + new_shot->img = 1; + new_shot->invuln = 1; + break; + case 2: + new_shot->img = 2; + new_shot->invuln = 1; + new_shot->t = 0; + new_shot->fire_time = 30; + new_shot->duration = 30; + new_shot->turn = 0.0; + new_shot->speed = 0.0; + break; + case 3: + new_shot->img = 3; + break; + case 4: + new_shot->img = 4; + new_shot->invuln = 1; + break; + case 5: + new_shot->img = 5; + new_shot->t = 0; + break; + case 6: + new_shot->img = 6; + new_shot->invuln = 1; + break; + case 7: + new_shot->img = 7; + new_shot->invuln = 1; + break; + case 8: + new_shot->img = 8; + new_shot->invuln = 1; + break; + } + if (training) { + new_shot->speed *= 0.8; + } + new_shot->next = bullet_stack; + bullet_stack = new_shot; + + total_bullets++; + return new_shot; +} + +struct bullet *FireLaser(int x, int y, struct enemy *firer, float dir, int fire_time, int duration, float turn, int dmg) +{ + int f_total; + struct bullet *b; + b = CreateBullet(x, y, firer, 2, dir, 0); + b->fire_time = fire_time; + b->duration = duration; + b->turn = turn; + b->shield_damage = dmg; + + if (training) { + f_total = b->fire_time + b->duration; + if (b->duration > 1) { + b->duration /= 2; + b->fire_time = f_total - b->duration; + } + b->shield_damage = (b->shield_damage + 1) / 2; + } + return b; +} + +void InitEnemySprites() +{ + enemy_sprites[0] = IMG_Load("dat/i/mons1.png"); + SDL_SetColorKey(enemy_sprites[0], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[1] = IMG_Load("dat/i/mons2.png"); + SDL_SetColorKey(enemy_sprites[1], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[2] = IMG_Load("dat/i/mons3.png"); + SDL_SetColorKey(enemy_sprites[2], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[3] = IMG_Load("dat/i/mons4.png"); + SDL_SetColorKey(enemy_sprites[3], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[4] = IMG_Load("dat/i/mons5.png"); + SDL_SetColorKey(enemy_sprites[4], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[5] = IMG_Load("dat/i/mons6.png"); + SDL_SetColorKey(enemy_sprites[5], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[6] = IMG_Load("dat/i/mons7.png"); + SDL_SetColorKey(enemy_sprites[6], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[7] = IMG_Load("dat/i/mons8.png"); + SDL_SetColorKey(enemy_sprites[7], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[8] = IMG_Load("dat/i/mons9.png"); + SDL_SetColorKey(enemy_sprites[8], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + enemy_sprites[9] = IMG_Load("dat/i/mons10.png"); + SDL_SetColorKey(enemy_sprites[9], SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + + reticle = IMG_Load("dat/i/reticle.png"); + SDL_SetColorKey(reticle, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + + inrange = IMG_Load("dat/i/inrange.png"); + SDL_SetColorKey(inrange, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + + invis_enemy = IMG_Load("dat/i/hidden_monster.png"); + SDL_SetColorKey(invis_enemy, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); +} + +void ActivateSingleEnemy(struct enemy *t) +{ + struct enemy *new_active; + + if (t->active) return; + + if (t->enemy_type != 10) { + if (t->enemy_type == 9) { + if (max_activate_dist < 10) return; + } else { + + if (t->enemy_type > (max_activate_dist / 5 + 2)) return; + + } + } + + new_active = t; + new_active->next_active = active_stack; + active_stack = new_active; + + active_enemies += 1; + t->active = 1; +} + +void XActivateSingleEnemy(struct enemy *t) +{ + if (rooms[t->room].room_type == 2) return; + if (rooms[t->room].room_type == 3) return; + + ActivateSingleEnemy(t); +} + +void InitEnemies() +{ + int c_room; + int cr_w, cr_h, cr_x, cr_y; + int room_size; + int n_enemies; + int i; + int trying; + int e_x, e_y; + int nx, ny; + + max_activate_dist = 0; + + InitEnemySprites(); + + for (ny = 0; ny < 20; ny++) { + for (nx = 0; nx < 20; nx++) { + enemy_loc_stack[nx][ny] = NULL; + } + } + + total_enemies = 0; + killed_enemies = 0; + total_bullets = 0; + active_enemies = 0; + total_gems = 0; + + for (i = 0; i < 3000; i++) { + room_active[i] = 0; + } + + if (game_load) { + ReadCreatureData(); + if (current_boss > 0) SoupUpEnemies(); + } else { + for (c_room = 1; c_room < 3000; c_room++) { + cr_x = rooms[c_room].x + 1; + cr_y = rooms[c_room].y + 1; + cr_w = rooms[c_room].w - 2; + cr_h = rooms[c_room].h - 2; + room_size = cr_w * cr_h; + + n_enemies = rand() % ((room_size / 4) + 1); + + if (rooms[c_room].room_type == 2) { + n_enemies = 0; + } + + if (rooms[c_room].room_type == 3) { + n_enemies += (n_enemies + room_size) / 2; + } + + if (rooms[c_room].room_type == 5) { + n_enemies = 50; + } + + while (n_enemies > 0) { + trying = 1; + while (trying) { + e_x = cr_x * 32 + 32 + rand() % (cr_w * 32 - 64 + 1); + e_y = cr_y * 32 + 32 + rand() % (cr_h * 32 - 64 + 1); + + if ((!IsSolid(Get( (e_x-16) /32, (e_y-16) /32)))&&(!IsSolid(Get( (e_x+16) /32, (e_y-16) /32)))) { + if ((!IsSolid(Get( (e_x-16) /32, (e_y+16) /32)))&&(!IsSolid(Get( (e_x+16) /32, (e_y+16) /32)))) { + n_enemies -= (CreateEnemy(e_x, e_y, c_room))->creationcost; + trying = 0; + } + } + } + } + if (c_room % 100 == 99) { + LoadingScreen(2, (float)c_room / 3000.0); + } + } + } + + +} + +int EnemyMovement(struct enemy *e, int move_x, int move_y) +{ + if (!IsSolid(Get( (move_x - 12)/32, (move_y - 12)/32))) { + if (!IsSolid(Get( (move_x + 12)/32, (move_y - 12)/32))) { + if (!IsSolid(Get( (move_x - 12)/32, (move_y + 12)/32))) { + if (!IsSolid(Get( (move_x + 12)/32, (move_y + 12)/32))) { + e->x = move_x; + e->y = move_y; + return 1; + } + } + } + } + return 0; +} + +// Only activate SOME enemies. A room can only be ZActivated once +void ZActivateEnemies(int room) +{ + struct enemy *t; + struct RoomConnection *rc; + + if (room_active[room]) return; + room_active[room] = 1; + + t = enemy_stack; + + while (t != NULL) { + // 1/4 chance of activating each enemy + if (rand()%4 == 0) { + if (t->room == room) { + if (t->active == 0) { + XActivateSingleEnemy(t); + } + } + } + t = t->next; + } + + // 1/3 chance of activating each adjacent room + rc = rooms[room].con; + while (rc != NULL) { + if (rand()%3 == 0) { + ZActivateEnemies(rc->c); + } + rc = rc->n; + } +} + +void ActivateEnemies(int room) +{ + struct enemy *t; + struct RoomConnection *rc; + + t = enemy_stack; + + if (rooms[room].s_dist > max_activate_dist) { + max_activate_dist = rooms[room].s_dist; + } + + while (t != NULL) { + if (t->room == room) { + if (t->active == 0) { + ActivateSingleEnemy(t); + + if (rooms[room].room_type == 3) { + t->teleport_v = (rand() % 1500) + 50; + } + } + } + t = t->next; + } + + // 1/2 chance of activating each adjacent room + rc = rooms[room].con; + while (rc != NULL) { + if (rand()%2 == 0) { + if ((rooms[rc->c].room_type != 2) && (rooms[rc->c].room_type != 3)) { + ZActivateEnemies(rc->c); + } + } + rc = rc->n; + } +} + +int CanEnterRoom(int room) +{ + if (room == 0) return 0; + if (rooms[room].room_type == 2) return 0; + if (rooms[room].room_type == 3) return 0; + if (rooms[room].room_type == 5) return 0; + if (rooms[room].room_type == 6) return 0; + + if (artifacts[11]) { + if (rooms[room].enemies > 3) { + return 0; + } + } + + return 1; +} +int CanLeaveRoom(int room) +{ + if (room == 0) return 0; + if (rooms[room].room_type == 2) return 0; + if (rooms[room].room_type == 3) return 0; + if (rooms[room].room_type == 5) return 0; + if (rooms[room].room_type == 6) return 0; + + return 1; +} + +int RecurseFind(struct enemy *e, int room, int depth) +{ + struct RoomConnection *follow; + int dpth; + int mindpth = 1000000; + follow = rooms[room].con; + + if (CanEnterRoom(room) == 0) return 0; + + if (room == player_room) return depth+1; + if (searched[room] == csearch) { + if (searchdist[room] < depth) + return 0; + } + if (depth > e->followdepth) return 0; + if ((e->last_room == room) && (e->p_last_room == player_room)) return 0; + + searched[room] = csearch; + searchdist[room] = depth; + + while (follow != NULL) { + if ((dpth = RecurseFind(e, follow->c, depth+1)) > 0) { + if (dpth < mindpth) { + mindpth = dpth; + } + } + follow = follow->n; + } + + if (mindpth != 1000000) { + return mindpth; + } + + return 0; + +} + +int FollowPlayer(struct enemy *e, struct RoomConnection **rcon) +{ + struct RoomConnection *follow; + int mindepth = 1000000; + int newdepth; + int mdist = 1000000; + int ndist = 0; + int rdepth[4000]; + + return 0; + follow = rooms[e->room].con; + + while (follow != NULL) { + if (follow->c == player_room) { + if (CanEnterRoom(follow->c)) { + *rcon = follow; + e->curr_follow = (*rcon)->c; + return 1; + } + } + follow = follow->n; + } + + // Recursively follow the player, to a certain depth + follow = rooms[e->room].con; + csearch = rand(); + + // Are we already following the player into a room? + + /*if (e->curr_follow != -1) { + // See if this room is the best FIRST + newdepth = RecurseFind(e, e->curr_follow, 0); + if (newdepth > 0) { + mindepth = newdepth; + *rcon = follow; + } + }*/ + + while (follow != NULL) { + if (CanEnterRoom(follow->c)) { + newdepth = RecurseFind(e, follow->c, 0); + rdepth[follow->c] = newdepth; + if (newdepth > 0) { + if (mindepth > newdepth) { + mindepth = newdepth; + *rcon = follow; + } + } + } + follow = follow->n; + } + if (mindepth != 1000000) { + follow = rooms[e->room].con; + while (follow != NULL) { + if (CanEnterRoom(follow->c)) { + newdepth = rdepth[follow->c]; + if (newdepth == mindepth) { + ndist = PlayerDist(follow->x*32+16, follow->y*32+16); + if (ndist < mdist) { + mdist = ndist; + *rcon = follow; + } + } + } + follow = follow->n; + } + + e->curr_follow = (*rcon)->c; + return mindepth; + } + return 0; +} + +void KillEnemy(struct enemy *t) +{ + static int lastkill = 0; + int ct; + + if (t->dying > 0) return; + if (t->teleport_v > 0) return; + if (t->delete_me) return; + + ct = SDL_GetTicks(); + + if ((ct - lastkill) > 100) { + SND_Pos("dat/a/enemyhit.wav", 128, PlayerDist(t->x, t->y)); + lastkill = ct; + } + + t->dying = 1; +} + +void ArtifactRoomUnlock(int room) +{ + struct enemy *e; + int x, y, rx, ry, rt; + int placed; + int tot_treasures; + + e = active_stack; + while (e != NULL) { + if ((e->delete_me == 0) && (e->room == room)) { + return; + } + e = e->next_active; + } + + // unlock doors + + for (y = 0; y < rooms[room].h; y++) { + for (x = 0; x < rooms[room].w; x++) { + rx = x + rooms[room].x; + ry = y + rooms[room].y; + rt = Get(rx, ry); + + if ((rt >= 21) && (rt <= 24)) { + Put(rx, ry, rt - 21 + 13, room); + } + } + } + + // place treasure + placed = 0; + tot_treasures = 2 + rand() % (rooms[room].s_dist / 8 + 1); + while (placed < tot_treasures) { + x = rooms[room].x + (rand() % (rooms[room].w - 2)); + y = rooms[room].y + (rand() % (rooms[room].h - 2)); + //printf("Attempting %d, %d\n", x, y); + if ((x+y)%2 == (placed>0)) { + //printf("Correct placement type\n"); + if (!IsSolid(Get(x, y))) { + //printf("Not solid\n"); + Put(x, y, 26, room); + placed++; + } + } + fflush(stdout); + } + + // sign room + rooms[room].room_type = 4; +} + +void EnemySound(int t, int dist) +{ + static int last_e_sound = 0; + static int last_delay = 150; + int curr_e_sound = SDL_GetTicks(); + + if ((curr_e_sound - last_delay) < last_e_sound) { + return; + } + + switch (t) { + case 0: + SND_Pos("dat/a/mons0shot.wav", 48, dist); + last_delay = 200; + break; + case 1: + SND_Pos("dat/a/mons1shot.wav", 112, dist); + last_delay = 500; + break; + case 2: + SND_Pos("dat/a/mons2shot.wav", 110, dist); + last_delay = 1000; + break; + case 3: + SND_Pos("dat/a/mons3shot.wav", 110, dist); + last_delay = 500; + break; + case 4: + SND_Pos("dat/a/mons4shot.wav", 110, dist); + last_delay = 900; + break; + case 5: + SND_Pos("dat/a/mons5shot.wav", 80, dist); + last_delay = 60; + break; + case 6: + SND_Pos("dat/a/mons6shot.wav", 110, dist); + last_delay = 1000; + break; + case 7: + SND_Pos("dat/a/mons7shot.wav", 110, dist); + last_delay = 600; + break; + case 8: + SND_Pos("dat/a/mons8shot.wav", 110, dist); + last_delay = 700; + break; + case 9: + SND_Pos("dat/a/mons9shot.wav", 110, dist); + last_delay = 242; + break; + case 10: + SND_Pos("dat/a/mons10shot.wav", 110, dist); + last_delay = 250; + break; + + + default: + break; + } + + last_e_sound = curr_e_sound; +} + +void MoveEnemy(struct enemy *e) +{ + int n_gems; + int i; + int move_x, move_y; + int door_x=0, door_y=0; + int enemy_fire_type; + int actual_lives; + int can_move = 0; + struct RoomConnection *con_traverse; + struct RoomConnection *rcon; + int nearest; + struct bullet *b; + e->t++; + float dp; + float dpf; + if (e->teleport_v > 0) { + e->teleport_v--; + return; + } + + if (e->enemy_type < 10) { + enemy_fire_type = e->enemy_type; + } else { + enemy_fire_type = rand()%10; + } + + if (e->dying == 0) { + if ((e->t % e->fire_rate) == 0) { + if (e->room == player_room) { + switch (enemy_fire_type) { + case 0: + EnemySound(0, PlayerDist(e->x, e->y)); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y), 1.5); + if (e->enemy_type==10) e->fire_rate = 20; + break; + + case 1: + EnemySound(1, PlayerDist(e->x, e->y)); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)+0.1, 2.1); + CreateBullet(e->x, e->y, e, 3, PlayerDir(e->x, e->y), 2.4); + CreateBullet(e->x, e->y, e, 3, PlayerDir(e->x, e->y)-0.1, 2.1); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y), 1.7); + if (e->enemy_type==10) e->fire_rate = 25; + break; + + case 2: + EnemySound(2, PlayerDist(e->x, e->y)); + for (dp = 0; dp < M_PI*2; dp += 0.25) { + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)+dp, 1.1); + } + dp = RandomDir(); + for (i = 0; i < 20; i++) { + CreateBullet(e->x, e->y, e, 0, dp, (float)i * 0.1 + 2.5); + } + if (e->enemy_type==10) e->fire_rate = 40; + break; + + case 3: + EnemySound(3, PlayerDist(e->x, e->y)); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y), 2.5); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)+0.1, 2.5); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)+0.2, 2.5); + CreateBullet(e->x, e->y, e, 1, PlayerDir(e->x, e->y), 1.5); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)-0.2, 2.2); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)-0.3, 2.2); + if (e->enemy_type==10) e->fire_rate = 24; + break; + + case 4: + EnemySound(4, PlayerDist(e->x, e->y)); + for (dp = 0; dp < M_PI * 0.66; dp += 0.2) { + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)+0.5 - dp, 1.8+(dp/2)); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)-0.4 + dp, 1.8+(dp/2)); + } + FireLaser(e->x, e->y, e, PlayerDir(e->x, e->y), 24, 4, 20.0, 6); + if (e->enemy_type==10) e->fire_rate = 32; + break; + + case 5: + EnemySound(5, PlayerDist(e->x, e->y)); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y), 4); + if (e->enemy_type==10) e->fire_rate = 2; + break; + + case 6: + EnemySound(6, PlayerDist(e->x, e->y)); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)+0.25, 6); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)+0.25, 4); + + CreateBullet(e->x, e->y, e, 5, PlayerDir(e->x, e->y) - 0.05, 4.99); + CreateBullet(e->x, e->y, e, 5, PlayerDir(e->x, e->y), 4.99); + CreateBullet(e->x, e->y, e, 5, PlayerDir(e->x, e->y) + 0.05, 4.99); + + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)-0.25, 5); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)-0.25, 3); + if (e->enemy_type==10) e->fire_rate = 10; + break; + + case 7: + EnemySound(7, PlayerDist(e->x, e->y)); + for (dp = 0; dp < M_PI * 0.66; dp += 0.1) { + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)+0.5 - dp, 1.8+(dp/2)); + CreateBullet(e->x, e->y, e, 3, PlayerDir(e->x, e->y)-0.4 + dp, 1.8+(dp/2)); + + CreateBullet(e->x, e->y, e, 3, PlayerDir(e->x, e->y)+0.5 - dp*2, 4+(dp*2)); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y)-0.4 + dp*2, 4+(dp*2)); + } + FireLaser(e->x, e->y, e, (float)e->t / 25.0, 16, 10, 0.08, 4); + FireLaser(e->x, e->y, e, (float)e->t / 25.0 + M_PI*2/4, 16, 10, 0.08, 4); + FireLaser(e->x, e->y, e, (float)e->t / 25.0 + M_PI*4/4, 16, 10, 0.08, 4); + FireLaser(e->x, e->y, e, (float)e->t / 25.0 + M_PI*6/4, 16, 10, 0.08, 4); + if (e->enemy_type==10) e->fire_rate = 27; + break; + + case 8: + EnemySound(8, PlayerDist(e->x, e->y)); + for (dp = 0; dp < 1; dp += 0.1) { + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y) + M_PI/2 + dp, 3 + dp/2); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y), 4 + dp); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y) - M_PI/2 - dp, 3 + dp/2); + } + CreateBullet(e->x, e->y, e, 6, (e->t / e->fire_rate)*0.7, 2); + if (e->enemy_type==10) e->fire_rate = 8; + break; + + case 9: + i = rand()%((rand()%(PlayerDist(e->x, e->y)+1))+1); + if (i < 15) { + EnemySound(9, PlayerDist(e->x, e->y)); + dpf = (float)(2000 - e->str) / 2500.0; + for (dp = 0; dp < 1; dp += dpf) { + (CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y) + 0.1 - dp/5.0, 3.5 + dp/3.0)); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y) + (2*M_PI / 3) + 0.1 - dp/5.0, 3.5 + dp/3.0); + CreateBullet(e->x, e->y, e, 0, PlayerDir(e->x, e->y) + (4*M_PI / 3) + 0.1 - dp/5.0, 3.5 + dp/3.0); + + CreateBullet(e->x, e->y, e, 4, PlayerDir(e->x, e->y) + 0.1 - dp/5.0, 7 + dp*5); + CreateBullet(e->x, e->y, e, 4, PlayerDir(e->x, e->y) + (2*M_PI / 3) + 0.1 - dp/5.0, 7 + dp*5); + CreateBullet(e->x, e->y, e, 4, PlayerDir(e->x, e->y) + (4*M_PI / 3) + 0.1 - dp/5.0, 7 + dp*5); + } + } + if (e->enemy_type==10) e->fire_rate = 21; + break; + + default: + break; + } + } + } + + if ((e->t % e->speed) == 0) { + if (player_room == e->room) { + e->m_exit = NULL; + if (e->t % (e->speed * (8 + rand()%6)) == 0) { + e->move_dir = (float)(rand()%256) / 256.0 * M_PI * 2.0; + } + move_x = e->x + cos(e->move_dir)*5; + move_y = e->y + sin(e->move_dir)*5; + + EnemyMovement(e, move_x, move_y); + } else { + if (CanLeaveRoom(e->room)) { + // Try to follow the player into the next room + + // Are we already moving towards an exit? + + if (e->m_exit != NULL) { + rcon = e->m_exit; + + door_x = (rcon->x + (rcon->x - rcon->x2))*32+16; + door_y = (rcon->y + (rcon->y - rcon->y2))*32+16; + + can_move = 1; + } else { + con_traverse = rooms[e->room].con; + nearest = PlayerDist(e->x, e->y); + rcon = NULL; + while (con_traverse != NULL) { + i = PlayerDist(con_traverse->x2 * 32 + 16, con_traverse->y2 * 32 + 16); + + if ((i < nearest) && CanEnterRoom(GetRoom(con_traverse->x2, con_traverse->y2))) { + nearest = i; + rcon = con_traverse; + } + con_traverse = con_traverse->n; + } + + if (rcon != NULL) { + door_x = (rcon->x + (rcon->x - rcon->x2))*32+16; + door_y = (rcon->y + (rcon->y - rcon->y2))*32+16; + e->m_exit = rcon; + can_move = 1; + } + } + + // So, can we move? + if (can_move) { + // Are we near the door? + if (( abs(door_x - e->x) + abs(door_y - e->y))<6) { + // Go through the door + e->last_room = e->room; + e->p_last_room = player_room; + e->x = (rcon->x2 + (rcon->x2 - rcon->x))*32+16; + e->y = (rcon->y2 + (rcon->y2 - rcon->y))*32+16; + rooms[e->room].enemies--; + e->room = (rcon->c); + rooms[e->room].enemies++; + e->curr_follow = -1; + + e->m_exit = NULL; + + } else { + // Move towards the door + e->move_dir = atan2(door_y - e->y, door_x - e->x); + move_x = e->x + cos(e->move_dir)*5; + move_y = e->y + sin(e->move_dir)*5; + + EnemyMovement(e, move_x, move_y); + } + } + } + } + } + } + + if (e->dying > 0) { + e->dying++; + if (e->dying == 3) { + b = bullet_stack; + while (b != NULL) { + if (!b->delete_me) { + if (b->firer == e) { + b->delete_me = 1; + CreateGem(b->x, b->y, b->room, (e->max_gems + e->min_gems) / 5 + (artifacts[2] * (e->max_gems + e->min_gems) / 4)); + } + } + b = b->next; + } + } + if (e->dying >= 20) { + if ((e->lives >= 4)&&training) { + actual_lives = e->lives * 3 / 4; + } else { + actual_lives = e->lives; + } + + e->deaths++; + if (e->deaths >= actual_lives) { + e->deaths--; + e->delete_me = 1; + e->dying = 0; + killed_enemies++; + rooms[e->room].enemies--; + n_gems = e->min_gems + rand()%(e->max_gems - e->min_gems + 1); + for (i = 0; i < n_gems; i++) { + CreateGem(e->x - 16 + rand()%32, e->y - 16 + rand()%32, e->room, 1+rand()%4 + (artifacts[2]*rand()%3)); + } + if (rooms[e->room].room_type == 3) { + ArtifactRoomUnlock(e->room); + } + } else { + e->dying = 0; + } + } + } +} + +void MoveBullet(struct bullet *e) +{ + int pdist; + struct bullet *n; + float fx, fy; + static int last_shield_hit_sound = 0; + int c_shield_hit_sound; + + e->t++; + + if ( (boss_fight_mode != 0) && (boss_fight_mode != 2) && (e->dying == 0) ) { + e->dying = 1; + } + + if (e->dying == 0) { + if (e->img == 7) { + if (proxy_seek) { + e->speed = 10; + e->img = 4; + e->dir = (e->dir + PlayerDir(e->x, e->y)) / 2; + } + } + + e->x += cos(e->dir) * e->speed; + e->y += sin(e->dir) * e->speed; + + if (e->img == 1) { + if (e->t % 20 == 19) { + if (e->t % 40 >= 20) + CreateBullet(e->x, e->y, e->firer, 3, e->dir + M_PI/2, e->speed * 0.75); + else + CreateBullet(e->x, e->y, e->firer, 3, e->dir - M_PI/2, e->speed * 0.75); + } + } + if (e->img == 6) { + if (e->t % 40 == 39) { + (FireLaser(e->x, e->y, e->firer, (float)e->t / 30.0, 20, 10, 0.05, 2))->parent = e; + (FireLaser(e->x, e->y, e->firer, (float)e->t / 30.0 + M_PI*2/3, 20, 10, 0.05, 2))->parent = e; + (FireLaser(e->x, e->y, e->firer, (float)e->t / 30.0 + M_PI*4/3, 20, 10, 0.05, 2))->parent = e; + } + } + + + if (e->img == 5) { + if (e->t < 100) { + if (e->t % 20 == 9) { + n = CreateBullet(e->x, e->y, e->firer, 5, e->dir + M_PI/4, e->speed); + n->natural_dir = e->dir; + n->t = e->t; + e->natural_dir = e->dir; + + e->dir -= M_PI / 4; + } + if (e->t % 20 == 19) { + e->dir = e->natural_dir; + } + } + if (e->t == 100) { + e->dir = PlayerDir(e->x, e->y); + e->speed *= 1.5; + } + } + + if (IsSolid(Get(e->x/32, e->y/32))) { + if (e->img == 3) { + if (e->room == player_room) { + e->dir = PlayerDir(e->x, e->y); + + if (IsSolid(Get((e->x + cos(e->dir) * e->speed * 1.5)/32, (e->y + sin(e->dir) * e->speed * 1.5)/32))) e->dying = 1; + + } else { + e->dying = 1; + } + } else { + e->dying = 1; + } + } + + if (e->img == 2) { + if (e->parent != NULL) { + e->x = e->parent->x; + e->y = e->parent->y; + } else { + if (e->firer != NULL) { + e->x = e->firer->x; + e->y = e->firer->y; + } + } + if (e->dying == 0) { + if ((e->t > e->fire_time)&&(e->t <= (e->fire_time + e->duration))) { + fx = e->x; + fy = e->y; + while (!IsSolid(Get((fx)/32, (fy)/32))) { + if (player_dying == 0) { + if (PlayerDist(fx, fy) < 30) { + // hits player shield + if ((player_shield > 0)&&(shield_hp > 0)) { + shield_hp -= e->shield_damage; + if (shield_hp >= 0) { + e->dying = 1; + break; + } else { + shield_hp = 0; + } + } + + if (PlayerDist(fx, fy) < 4 - (2 * artifacts[5])) { + player_dying = 1; + SND_Pos("dat/a/playerhurt.wav", 128, 0); + e->dying = 1; + break; + } + } + } + fx += cos(e->dir)*2; + fy += sin(e->dir)*2; + } + } + if (e->turn > 10) { + if (e->dir > PlayerDir(e->x, e->y)) { + e->dir -= (e->dir - PlayerDir(e->x, e->y)) / (e->turn - 9.00); + e->dir -= (e->dir - PlayerDir(e->x, e->y)) / (e->turn - 9.00); + } else { + e->dir += (PlayerDir(e->x, e->y) - e->dir) / (e->turn - 9.00); + } + } else { + e->dir += e->turn; + } + if (e->t > (e->fire_time + e->duration)) { + e->dying = 1; + } + } + } else { + + pdist = PlayerDist(e->x, e->y); + + if (pdist < 30) { + if (player_dying == 0) { + if (player_shield > 0) { + if (shield_hp > 0) { + shield_hp--; + if (e->img == 4) { + e->dying = 1; + } else { + e->dir += M_PI; + c_shield_hit_sound = SDL_GetTicks(); + + if ((c_shield_hit_sound - 150) > last_shield_hit_sound) { + SND_Pos("dat/a/shieldhit.wav", 50, 0); + last_shield_hit_sound = c_shield_hit_sound; + } + while (PlayerDist(e->x, e->y) < 30) { + e->x += cos(e->dir) * e->speed; + e->y += sin(e->dir) * e->speed; + } + } + } + } + } + } + if (e->dying == 0) { + if (pdist < 6 - (2 * artifacts[5])) { + if (player_dying == 0) { + SND_Pos("dat/a/playerhurt.wav", 128, 0); + player_dying = 1; + } + } + } + } + } + + if ((e->dying == 1) && (e->img == 8)) { + SpawnLaser(e->x - cos(e->dir) * e->speed, e->y - sin(e->dir) * e->speed, PlayerDir(e->x, e->y), 10, 10, 0.0, player_shield / 6); + } + + if (GetRoom((e->x)/32, (e->y)/32) != e->room) { + e->delete_me = 1; + } + + if (e->dying > 0) { + e->dying++; + + if (e->dying >= 10) { + e->delete_me = 1; + e->dying = 0; + } + } +} + +void DrawEnemy(struct enemy *e, SDL_Surface *scr) +{ + SDL_Rect draw_pos; + SDL_Rect surf_pos; + static SDL_Surface *teleflash = NULL; + + if (e->delete_me) return; + draw_pos.x = e->x - e->image->h/2/e->lives - scroll_x; + draw_pos.y = e->y - e->image->h/2/e->lives - scroll_y; + + surf_pos.x = e->blit_pos; + surf_pos.y = e->image->h/e->lives*e->deaths; + surf_pos.w = e->image->h/e->lives; + surf_pos.h = e->image->h/e->lives; + + if (e->teleport_v < 8) { + + if (e->dying == 0) { + SDL_BlitSurface(e->image, &surf_pos, scr, &draw_pos); + } else { + if ((e->deaths+1) >= e->lives) { + surf_pos.w = e->image->h/e->lives * (20 - e->dying) / 20; + surf_pos.h = surf_pos.w; + surf_pos.x += (e->image->h/e->lives - surf_pos.w)/2; + surf_pos.y += (e->image->h/e->lives - surf_pos.w)/2; + draw_pos.x += (e->image->h/e->lives - surf_pos.w)/2; + draw_pos.y += (e->image->h/e->lives - surf_pos.w)/2; + } else { + surf_pos.w = e->image->h/e->lives * (20 - e->dying/2) / 20; + surf_pos.h = surf_pos.w; + surf_pos.x += (e->image->h/e->lives - surf_pos.w)/2; + surf_pos.y += (e->image->h/e->lives - surf_pos.w)/2; + draw_pos.x += (e->image->h/e->lives - surf_pos.w)/2; + draw_pos.y += (e->image->h/e->lives - surf_pos.w)/2; + } + SDL_BlitSurface(e->image, &surf_pos, scr, &draw_pos); + } + + if (((e->t % 8) == 1) && (!game_paused)) { + e->blit_pos = (e->blit_pos + e->image->h/e->lives) % e->image->w; + } + + draw_pos.x = e->x - e->image->h/e->lives/2 - scroll_x; + draw_pos.y = e->y - e->image->h/e->lives/2 - scroll_y; + + draw_pos.x -= (128-e->image->h/e->lives)/2; + draw_pos.y -= (128-e->image->h/e->lives)/2; + + if (magic_circuit >= e->str) { + SDL_BlitSurface(reticle, NULL, scr, &draw_pos); + } + + draw_pos.x = e->x - e->image->h/e->lives/2 - scroll_x; + draw_pos.y = e->y - e->image->h/e->lives/2 - scroll_y; + + draw_pos.x -= (128-e->image->h/e->lives)/2; + draw_pos.y -= (128-e->image->h/e->lives)/2; + + if (sqrt(sqr(e->x - player_x) + sqr(e->y - player_y)) < circuit_range) { + SDL_BlitSurface(inrange, NULL, scr, &draw_pos); + } + } + + if (e->teleport_v < 24) { + if (teleflash == NULL) { + teleflash = IMG_Load("dat/i/teleflash.png"); + SDL_SetColorKey(teleflash, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + } + + surf_pos.x = 48*((e->teleport_v) / 3); + surf_pos.y = 0; + surf_pos.w = 48; + surf_pos.h = 48; + + draw_pos.x = e->x - 24 - scroll_x; + draw_pos.y = e->y - 24 - scroll_y; + SDL_BlitSurface(teleflash, &surf_pos, scr, &draw_pos); + } +} + +void DrawBullet(struct bullet *b) +{ + int i; + int x1, y1, x2, y2, xo1, yo1; + int z; + float fx, fy; + static SDL_Surface *d_star_small = NULL, *d_star_big = NULL, *d_star_ls = NULL; + SDL_Rect draw_pos, surf_pos; + if (b->delete_me) return; + if (b->img == 0) { + if (b->dying > 0) { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 6+(b->dying / 2), b->dying*10); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 3+(b->dying / 2), 255-b->dying*10); + } else { + if ((b->t % 8) < 4) { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 6, 0); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 4, 255); + } else { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 6, 255); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 4, 0); + } + } + } + if (b->img == 1) { + if (d_star_big == NULL) { + d_star_big = IMG_Load("dat/i/star1.png"); + SDL_SetColorKey(d_star_big, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + } + + if (b->dying == 0) { + surf_pos.x = b->x - 16 - scroll_x; + surf_pos.y = b->y - 16 - scroll_y; + draw_pos.x = (b->t % 8)*32; + draw_pos.y = 0; + draw_pos.w = 32; + draw_pos.h = 32; + SDL_BlitSurface(d_star_big, &draw_pos, screen, &surf_pos); + } else { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 4+(b->dying), b->dying*10); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 2+(b->dying), 255-b->dying*10); + } + } + if (b->img == 2) { + // IMMA CHARGIN MAH LAZER + if (b->t <= b->fire_time) { + z = (b->t * 150 / (b->fire_time+1)) + 80; + + fx = b->x; + fy = b->y; + while (!IsSolid(Get((fx)/32, (fy)/32))) { + DrawRect(fx-1-scroll_x, fy-1-scroll_y, 4, 4, z/2); + fx += cos(b->dir)*2; + fy += sin(b->dir)*2; + + if ((player_shield > 0)&&(shield_hp > 0)&&(PlayerDist(fx, fy) < 30)) break; + if (PlayerDist(fx, fy) < 4) break; + } + DrawCircle(b->x - scroll_x, b->y - scroll_y, 5, (z+255)/2.4); + DrawCircle(fx-cos(b->dir)*2 - scroll_x, fy-sin(b->dir)*2 - scroll_y, 4, z/2); + + fx = b->x; + fy = b->y; + while (!IsSolid(Get((fx)/32, (fy)/32))) { + DrawRect(fx-scroll_x, fy-scroll_y, 2, 2, z); + fx += cos(b->dir)*2; + fy += sin(b->dir)*2; + + if ((player_shield > 0)&&(shield_hp > 0)&&(PlayerDist(fx, fy) < 30)) break; + if (PlayerDist(fx, fy) < 4) break; + } + + DrawCircle(b->x - scroll_x, b->y - scroll_y, 3, (z+255)/2); + DrawCircle(fx-cos(b->dir)*2 - scroll_x, fy-sin(b->dir)*2 - scroll_y, 2, z); + } + + // SHOOP DA WHOOP + if ((b->t > b->fire_time) && (b->t <= b->fire_time + b->duration)) { + z = 255 - ((b->t - b->fire_time) * 200 / (b->duration+1)); + + fx = b->x; + fy = b->y; + while (!IsSolid(Get((fx)/32, (fy)/32))) { + DrawRect(fx-3-scroll_x, fy-3-scroll_y, 8, 8, z*2/3); + fx += cos(b->dir)*2; + fy += sin(b->dir)*2; + + if ((player_shield > 0)&&(shield_hp > 0)&&(PlayerDist(fx, fy) < 30)) break; + if (PlayerDist(fx, fy) < 4) break; + } + DrawCircle(b->x - scroll_x, b->y - scroll_y, 9, (z+255)/2.4); + DrawCircle(fx-cos(b->dir)*2 - scroll_x, fy-sin(b->dir)*2 - scroll_y, 8, z*2/3); + fx = b->x; + fy = b->y; + while (!IsSolid(Get((fx)/32, (fy)/32))) { + DrawRect(fx-2-scroll_x, fy-2-scroll_y, 6, 6, z); + fx += cos(b->dir)*2; + fy += sin(b->dir)*2; + + x1 = fx + rand()%12 - rand()%12 - scroll_x; + y1 = fy + rand()%12 - rand()%12 - scroll_y; + DrawRect(x1, y1, 2, 2, rand()%(z+1)); + + if ((player_shield > 0)&&(shield_hp > 0)&&(PlayerDist(fx, fy) < 30)) break; + if (PlayerDist(fx, fy) < 4) break; + } + + DrawCircle(b->x - scroll_x, b->y - scroll_y, 7, (z+255)/2); + DrawCircle(fx-cos(b->dir)*2 - scroll_x, fy-sin(b->dir)*2 - scroll_y, 6, z); + } + } + if (b->img == 3) { + if (d_star_small == NULL) { + d_star_small = IMG_Load("dat/i/star2.png"); + SDL_SetColorKey(d_star_small, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + } + if (b->dying == 0) { + surf_pos.x = b->x - 8 - scroll_x; + surf_pos.y = b->y - 8 - scroll_y; + draw_pos.x = (b->t % 8)*16; + draw_pos.y = 0; + draw_pos.w = 16; + draw_pos.h = 16; + SDL_BlitSurface(d_star_small, &draw_pos, screen, &surf_pos); + } else { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 4+(b->dying/2), b->dying*10); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 2+(b->dying/2), 255-b->dying*10); + } + } + if (b->img == 4) { + if (b->dying == 0) { + x1 = b->x + cos(b->dir)*(2) - scroll_x; + y1 = b->y + sin(b->dir)*(2) - scroll_y; + + x2 = b->x - cos(b->dir)*(80) - scroll_x; + y2 = b->y - sin(b->dir)*(80) - scroll_y; + for (i = 0; i < 40; i++) { + xo1 = (x2 - x1)*i/40 + x1; + yo1 = (y2 - y1)*i/40 + y1; + + z = (i/2); + if (i > 10) z = (40 - i)/7; + + DrawRect(xo1 - z - 2, yo1 - z - 2, z*2 + 4, z*2 + 4, 200); + } + for (i = 0; i < 40; i++) { + xo1 = (x2 - x1)*i/40 + x1; + yo1 = (y2 - y1)*i/40 + y1; + + z = (i/2); + if (i > 10) z = (40 - i)/7; + + DrawRect(xo1 - z, yo1 - z, z*2, z*2, 255); + } + } else{ + DrawCircleEx(b->x - scroll_x, b->y - scroll_y, b->dying * 2 + 1, b->dying * 1.75 - 1, 230); + DrawCircleEx(b->x - scroll_x, b->y - scroll_y, b->dying * 2, b->dying * 1.75, 255); + } + } + if (b->img == 5) { + if (b->dying > 0) { + i = 4+(b->dying / 5); + DrawRect(b->x - scroll_x - (i-1/2), b->y - scroll_y - (i-1/2), i, i, b->dying*10); + i = 2+(b->dying / 5); + DrawRect(b->x - scroll_x - (i-1/2), b->y - scroll_y - (i-1/2), i, i, 255-b->dying*10); + } else { + if ((b->t % 8) < 4) { + DrawRect(b->x - scroll_x - 1, b->y - scroll_y - 1, 4, 4, 0); + DrawRect(b->x - scroll_x, b->y - scroll_y, 2, 2, 255); + } else { + DrawRect(b->x - scroll_x - 1, b->y - scroll_y - 1, 4, 4, 255); + DrawRect(b->x - scroll_x, b->y - scroll_y, 2, 2, 0); + } + } + } + if (b->img == 6) { + if (d_star_ls == NULL) { + d_star_ls = IMG_Load("dat/i/star3.png"); + SDL_SetColorKey(d_star_ls, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + } + if (b->dying == 0) { + surf_pos.x = b->x - 16 - scroll_x; + surf_pos.y = b->y - 16 - scroll_y; + draw_pos.x = (b->t % 8)*32; + draw_pos.y = 0; + draw_pos.w = 32; + draw_pos.h = 32; + SDL_BlitSurface(d_star_ls, &draw_pos, screen, &surf_pos); + } else { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 4+(b->dying), 255-b->dying*10); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 2+(b->dying), b->dying*10); + } + } + + if (b->img == 7) { + if ((b->t % 8) < 4) { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 8, 0); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 5, 255); + } else { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 8, 255); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 5, 0); + } + } + if (b->img == 8) { + if (b->dying > 0) { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 12+(b->dying), b->dying*10); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 6+(b->dying), 255-b->dying*10); + } else { + if ((b->t % 6) < 3) { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 12, 0); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 8, 255); + } else { + DrawCircle(b->x - scroll_x, b->y - scroll_y, 12, 255); + DrawCircle(b->x - scroll_x, b->y - scroll_y, 8, 0); + } + } + } +} + +void DrawGem(struct diamond *g) +{ + static SDL_Surface *d_sprite = NULL; + int gemtype = 0; + static char hp_icon[2]; + SDL_Rect draw_pos; + SDL_Rect surf_pos; + unsigned char fxp = (SDL_GetTicks() / 300)%2 ? 255 : 0; + + if (g->delete_me) return; + + if (d_sprite == NULL) { + d_sprite = IMG_Load("dat/i/gem.png"); + SDL_SetColorKey(d_sprite, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + hp_icon[0] = 3; + hp_icon[1] = 0; + } + + if (g->value == 1) gemtype = 2; + if ((g->value > 1)&&(g->value < 5)) gemtype = 1; + if (g->value >= 5) gemtype = 0; + + if (g->value != 31337) { + + surf_pos.x = g->x - 4 - scroll_x; + surf_pos.y = g->y - 4 - scroll_y; + draw_pos.x = (g->t % 4) * 8 + 32 * gemtype; + draw_pos.y = 0; + draw_pos.w = 8; + draw_pos.h = 8; + + SDL_BlitSurface(d_sprite, &draw_pos, screen, &surf_pos); + } else { + DrawCircle(g->x - scroll_x, g->y - scroll_y, 6, (rand()%64) ^ fxp); + draw_text(g->x - 4 - scroll_x, g->y - 4 - scroll_y, hp_icon, (200+rand()%56) ^ fxp); + } + + g->t++; +} + +void xprintf(char *s) +{ + printf(s); + fflush(stdout); +} + +void DrawInvisible(int x, int y) +{ + SDL_Rect dest; + dest.x = x - scroll_x - 24; + dest.y = y - scroll_y - 24; + SDL_BlitSurface(invis_enemy, NULL, screen, &dest); +} + +void DrawEntities() +{ + struct enemy *t; + struct bullet *b; + struct diamond *g; + struct enemyloc *els; + + if ((rooms[player_room].room_type != 3)&&(rooms[player_room].room_type != 2)) { + // Draw gems + + g = room_gems[player_room]; + while (g != NULL) { + if ((g->room == player_room)&&(g->delete_me == 0)&&(g->value != 31337)) { + DrawGem(g); + } + g = g->next_in_room; + } + g = room_gems[player_room]; + while (g != NULL) { + if ((g->room == player_room)&&(g->delete_me == 0)&&(g->value == 31337)) { + DrawGem(g); + } + g = g->next_in_room; + } + } + + t = active_stack; + while (t != NULL) { + if (!t->delete_me) { + if ((t->room == player_room) && (t->enemy_type != 9)) { + DrawEnemy(t, screen); + } + } + t = t->next_active; + } + + // Draw invisible enemies (if possible) + if (artifacts[6]) { + // Draw the actives + t = active_stack; + while (t != NULL) { + if (!t->delete_me) + { + if (((!t->delete_me)&&((t->room != player_room)||(t->enemy_type == 9)))) { + DrawInvisible(t->x, t->y); + } + } + t = t->next_active; + } + // Draw the inactives + if (!artifacts[11]) { + els = GetEnemyLoc(scroll_x, scroll_y); + while (els != NULL) { + t = els->e; + if (((!t->delete_me)&&((t->room != player_room)||(t->enemy_type == 9)))&&(t->active == 0)) { + if ((t->x+24 >= scroll_x) && (t->y+24 >= scroll_y)) { + if (t->x-24 <= (scroll_x+639)) { + if (t->y-24 <= (scroll_y+479)) { + if ((rooms[t->room].room_type != 2) && (rooms[t->room].room_type != 3)) { + DrawInvisible(t->x, t->y); + } + } + } + } + } + els = els->n; + } + } + } + + b = bullet_stack; + while (b != NULL) { + if (!b->delete_me) { + if (b->room == player_room) { + DrawBullet(b); + } + } + b = b->next; + } +} + +void MoveEntities() +{ + struct enemy *t; + struct bullet *b; + struct diamond *g; + + struct bullet *b_del; + struct diamond *g_del; + + if ((rooms[player_room].room_type != 3)&&(rooms[player_room].room_type != 2)) { + // gem stuff + g = room_gems[player_room]; + while (g != NULL) { + if ((g->room == player_room)&&(g->delete_me == 0)) { + if (artifacts[7]) { + g->x += (player_x+4 - g->x)/10; + g->y += (player_y+12 - g->y)/10; + } + if (PlayerDist(g->x, g->y) < 20) { + g->delete_me = 1; + total_gems--; + + if (g->value == 31337) { + if (player_hp < (3 + (player_shield == 30)*3)) { + SND_Pos("dat/a/crystal.wav", 64, 0); + player_hp++; + } else { + if (!training) { + SND_Pos("dat/a/tone.wav", 64, 0); + + if (player_lives == 1) { + player_lives_part += 15; + } else { + if (player_lives < 10) { + player_lives_part += 4; + } else { + player_lives_part += 1; + } + } + if (player_lives_part >= 88) { + player_lives_part -= 88; + player_lives += 1; + SND_Pos("dat/a/crystal2.wav", 100, 0); + } + } + } + + } else { + player_gems += g->value; + } + } + } + g = g->next_in_room; + } + } + + t = active_stack; + while (t != NULL) { + if (!t->delete_me) { + if ((rooms[t->room].room_type != 3) || (t->room == player_room)) + MoveEnemy(t); + } + t = t->next_active; + } + + b = bullet_stack; + while (b != NULL) { + if (!b->delete_me) { + MoveBullet(b); + } + b = b->next; + } + + // delete old bullets + b = bullet_stack; + if ((b != NULL)&&(b->delete_me)) { + b_del = b; + b = b->next; + free(b_del); + bullet_stack = b; + } + while (b != NULL) { + if (b->next != NULL) { + if (b->next->delete_me) { + b_del = b->next; + + b->next = b->next->next; + free(b_del); + } + } + b = b->next; + } + + // delete old gems + g = gem_stack; + if ((g != NULL)&&(g->delete_me)) { + // Remove room reference + + if (g == room_gems[g->room]) { + room_gems[g->room] = g->next_in_room; + if (room_gems[g->room] != NULL) { + room_gems[g->room]->prv_in_room = NULL; + } + } else { + if (g->prv_in_room != NULL) { + g->prv_in_room->next_in_room = g->next_in_room; + } + + if (g->next_in_room != NULL) { + g->next_in_room->prv_in_room = g->prv_in_room; + } + } + + g_del = g; + g = g->next; + free(g_del); + gem_stack = g; + } + while (g != NULL) { + if (g->next != NULL) { + if (g->next->delete_me) { + // Remove room reference + assert( (g->next->prv_in_room != NULL) || (room_gems[g->next->room] == g->next) ); + + if (g->next == room_gems[g->next->room]) { + room_gems[g->next->room] = g->next->next_in_room; + if (room_gems[g->next->room] != NULL) { + room_gems[g->next->room]->prv_in_room = NULL; + } + } else { + g->next->prv_in_room->next_in_room = g->next->next_in_room; + if (g->next->next_in_room != NULL) { + g->next->next_in_room->prv_in_room = g->next->prv_in_room; + } + } + g_del = g->next; + + g->next = g->next->next; + free(g_del); + } + } + g = g->next; + } + + // delete old monsters + t = active_stack; + if (t != NULL) { + while (t->delete_me) { + t = t->next_active; + active_stack = t; + + if (t == NULL) break; + } + } + while (t != NULL) { + if (t == active_stack) + assert(t->delete_me == 0); + else + assert(!t->delete_me); + + if (t->next_active != NULL) { + while (t->next_active->delete_me) { + t->next_active = t->next_active->next_active; + if (t->next_active == NULL) break; + } + } + t = t->next_active; + } +} + +void HurtEnemies(int x, int y, int range, int power) +{ + struct enemy *t; + int e_range; + + t = active_stack; + while (t != NULL) { + e_range = sqrt(sqr(t->x - x) + sqr(t->y - y)); + if (e_range < range) { + if (power >= t->str) { + if (t->room == GetRoom(x/32, y/32)) { + KillEnemy(t); + } + } + } + t = t->next_active; + } +} + +void CircuitBullets(int x, int y, int r) +{ + struct bullet *b; + b = bullet_stack; + while (b != NULL) { + if (!b->delete_me) { + if (b->dying == 0) { + if (b->invuln == 0) { + if (sqrt(sqr(b->x - x) + sqr(b->y - y)) < r) { + b->dying = 1; + } + } + } + } + b = b->next; + } +} + +void CrystalSummon() +{ + struct diamond *g; + int rg_x, rg_y; + int i; + + g = gem_stack; + + for (i = 0; i < 3000; i++) { + room_gems[i] = NULL; + } + + while (g != NULL) { + if (!g->delete_me) { + if (rooms[g->room].room_type != 3) { + g->room = player_room; + rg_x = rooms[player_room].x * 32 + 32 + rand()%(rooms[player_room].w*32-64); + rg_y = rooms[player_room].y * 32 + 32 + rand()%(rooms[player_room].h*32-64); + if (player_room == 0) { + rg_x = (rooms[player_room].x+5) * 32 + 32 + rand()%(8*32); + rg_y = (rooms[player_room].y+5) * 32 + 32 + rand()%(5*32); + } + while (IsSolid(Get(rg_x/32, rg_y/32))) { + rg_x = rooms[player_room].x * 32 + 32 + rand()%(rooms[player_room].w*32-64); + rg_y = rooms[player_room].y * 32 + 32 + rand()%(rooms[player_room].h*32-64); + if (player_room == 0) { + rg_x = (rooms[player_room].x+5) * 32 + 32 + rand()%(8*32); + rg_y = (rooms[player_room].y+5) * 32 + 32 + rand()%(5*32); + } + } + g->x = rg_x; + g->y = rg_y; + } + } + g->next_in_room = room_gems[g->room]; + g->prv_in_room = NULL; + if (room_gems[g->room] != NULL) { + room_gems[g->room]->prv_in_room = g; + } + room_gems[g->room] = g; + + g = g->next; + } +} + +void ActivateRand() +{ + struct enemy *e; + struct enemy *en[20000]; + int ent = 0; + + e = enemy_stack; + + while (e != NULL) { + if (e->delete_me == 0) { + if (e->active == 0) { + en[ent++] = e; + } + } + e = e->next; + } + if (ent > 0) { + e = en[rand()%ent]; + + XActivateSingleEnemy(e); + } +} + +void ClearBossBullets() +{ + struct bullet *b = bullet_stack; + while (b != NULL) { + if (!b->delete_me) { + if (b->firer == NULL) { + b->delete_me = 1; + } + } + b = b->next; + } +} + +void SpawnBullet(int x, int y, int bullet_type, float dir, float spd, int invuln) +{ + struct bullet *b; + + if ( (current_boss == 3) && (player_shield == 30) && (boss_fight_mode == 2) ) { + spd *= 1.2; + } + b = CreateBullet(x, y, NULL, bullet_type, dir, spd); + if (invuln) { + b->invuln = 1; + } +} +void SpawnLaser(int x, int y, float dir, int fire_time, int duration, float turn, int dmg) +{ + FireLaser(x, y, NULL, dir, fire_time, duration, turn, dmg); +} + +void CullEnemies(int nth) +{ + struct enemy *e; + int i = 0; + + e = enemy_stack; + + while (e != NULL) { + if (e->delete_me == 0) { + if (rooms[e->room].room_type == 0) { + if ( (i % nth) == (nth - 1)) { + e->delete_me = 1; + killed_enemies++; + e->dying = 0; + rooms[e->room].enemies--; + } + i++; + } + } + e = e->next; + } +} + +void SoupUpEnemies() +{ + struct enemy *e; + int str_limit; + float str_multiplier; + float fr_divider; + + e = enemy_stack; + str_limit = 1500; + if (circuit_size > 1500) { + str_limit = 1500; + } + + str_multiplier = 1.0 + (1.0/3.0)*(float)current_boss; + fr_divider = 1.0 + (2.0/3.0)*(float)current_boss; + + while (e != NULL) { + if (e->delete_me == 0) { + if (e->enemy_type != 10) { + if (e->str < str_limit) { + if ((e->str * 2) < str_limit) { + e->str = e->str * str_multiplier; + } else { + e->str = str_limit; + } + } + e->fire_rate = (int)((float)e->fire_rate / fr_divider) + 1; + e->speed = (int)((float)e->speed / fr_divider) + 1; + e->min_gems *= str_multiplier; + e->max_gems *= str_multiplier; + } + } + e = e->next; + } +} + +void CurseSingleEnemy(struct enemy *e) +{ + static int ActiveRooms[3000]; + static int NActiveRooms = 0; + int i; + int rm; + + if (NActiveRooms == 0) { + for (i = 0; i < 3000; i++) { + if ((rooms[i].room_type == 0) || (rooms[i].room_type == 4)) { + ActiveRooms[NActiveRooms++] = i; + } + } + } + + rm = ActiveRooms[rand()%NActiveRooms]; + while ((rooms[rm].enemies > 3) || (rooms[rm].visited == 0)) { + rm = ActiveRooms[rand()%NActiveRooms]; + } + + e->x = rooms[rm].w * 16 + rooms[rm].x * 32; + e->y = rooms[rm].h * 16 + rooms[rm].y * 32; + rooms[e->room].enemies--; + e->room = rm; + rooms[e->room].enemies++; + + e->image = enemy_sprites[9]; + e->lives = 8; + e->str = 500; + e->speed = 1; + e->fire_rate = (rand()%4)+1; + e->min_gems = 5000; + e->max_gems = 6000; + e->followdepth = 12; + e->creationcost = 6; + e->enemy_type = 10; + + ActivateSingleEnemy(e); +} + +void CurseEnemies() +{ + struct enemy *e; + int i = 0; + + e = enemy_stack; + + while (e != NULL) { + if (e->delete_me == 0) { + if ( (i % 5) == (4)) { + CurseSingleEnemy(e); + } else { + e->delete_me = 1; + killed_enemies++; + + e->dying = 0; + rooms[e->room].enemies--; + } + i++; + } + e = e->next; + } +} diff --git a/src/demon.h b/src/demon.h new file mode 100755 index 0000000..c79455a --- /dev/null +++ b/src/demon.h @@ -0,0 +1,63 @@ +// +// demon.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// Exposes demon.c functionality and types + +#ifndef DEMON_H +#define DEMON_H + +void InitEnemies(); +void ActivateEnemies(int room); +void DrawEntities(); +void MoveEntities(); +void HurtEnemies(int x, int y, int range, int power); +void CircuitBullets(int x, int y, int r); +void CreateGem(int x, int y, int r, int v); + +extern int total_enemies, killed_enemies, total_gems; + +void WriteCreatureData(); +void WriteEnemyData(); +void WriteGemData(); +void ReadCreatureData(); +void ReadEnemyData(); +void ReadGemData(); +void CrystalSummon(); + +void ActivateRand(); + +void DestroyThings(); + +void EnemySound(int t, int dist); + +void SpawnBullet(int x, int y, int bullet_type, float dir, float spd, int invuln); +void SpawnLaser(int x, int y, float dir, int fire_time, int duration, float turn, int dmg); + +void ClearBossBullets(); + +float PlayerDir(int x, int y); +int PlayerDist(int x, int y); + +void CullEnemies(int nth); +void SoupUpEnemies(); + +void CurseEnemies(); +#endif diff --git a/src/ending.c b/src/ending.c new file mode 100755 index 0000000..d759ab1 --- /dev/null +++ b/src/ending.c @@ -0,0 +1,699 @@ +// +// ending.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include +#include + +#include "levelblit.h" +#include "audio.h" +#include "boss.h" +#include "mapgen.h" + +void DrawScrolly(int t); +void DrawPText(int t); +void DrawSText(int t); +void DrawSTextV(int t); +void DrawCircuitFlash(int t, int method); +void DrawStream(int t); + +void InitParticleStorm(); +void RunParticleStorm(int offset); + +SDL_Surface *streamspr = NULL, *glitter = NULL; + +SDL_Color ending_pal[256]; + +void UpdatePalette() +{ + SDL_SetPalette(screen, SDL_PHYSPAL, ending_pal, 0, 256); +} + +void DrawCredits(); + +int credits_scroll = 0; + +int EndingEvents() +{ + static SDL_Event event; + + player_room = 0; + current_boss = 3; + boss_fight_mode = 4; + + MusicUpdate(); + + while (SDL_PollEvent(&event)) { + if (event.type == SDL_KEYDOWN) { + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + return 1; + break; + default: + break; + } + } + if (event.type == SDL_QUIT) { + return 1; + } + } + + return 0; +} + +void ShowEnding() +{ + int i; + + if (streamspr == NULL) { + streamspr = IMG_Load("dat/i/stream.png"); + SDL_SetColorKey(streamspr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + glitter = IMG_Load("dat/i/glitter.png"); + SDL_SetColorKey(glitter, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + } + + for (i = 0; i < 500; i += 1) { + if (((i % 60) >= 24)&&((i % 60) < 34)) { + DrawCircuitFlash((i % 60) - 24, 0); + } else { + DrawScrolly(i); + } + EndCycle(0); + if (EndingEvents()) return; + } + for (i = 0; i < 30; i++) { + DrawCircuitFlash(i, 1); + + EndCycle(0); + if (EndingEvents()) return; + } + SDL_FillRect(screen, NULL, 255); + for (i = 0; i < 350; i++) { + DrawPText(i); + EndCycle(0); + if (EndingEvents()) return; + } + + Paint(0, 0, 22, 27, "dat/d/cstream.loc"); + + if (player_shield < 30) { + for (i = 0; i < 400; i++) { + DrawStream(i); + EndCycle(0); + if (EndingEvents()) return; + } + InitParticleStorm(); + for (i = 0; i < 240; i++) { + RunParticleStorm(240-i); + EndCycle(0); + if (EndingEvents()) return; + } + for (i = 0; i < 60; i++) { + RunParticleStorm(0); + EndCycle(0); + if (EndingEvents()) return; + } + for (i = 0; i < 180; i++) { + RunParticleStorm(i*3); + EndCycle(0); + if (EndingEvents()) return; + } + for (i = 0; i < 500; i++) { + DrawSText(i); + EndCycle(0); + if (EndingEvents()) return; + } + } else { + for (i = 0; i < 250; i++) { + DrawStream(i); + EndCycle(0); + if (EndingEvents()) return; + } + for (i = 0; i < 500; i++) { + DrawSTextV(i); + EndCycle(0); + if (EndingEvents()) return; + } + } + + credits_scroll = 0; + for (;;) { + DrawCredits(); + EndCycle(0); + if (EndingEvents()) return; + } +} + +char *SText[15] = { "Merit released the locks on the PSI flowing through the Dome,", + "releasing the flow of PSI into the atmosphere.", + "", + "The Orcus Dome was originally built to centralise the limited", + "PSI available to everyone. However, this made the existing", + "reserves more vulnerable to malicious PSI users", + "", + "While other PSI users initially resented Merit for his rash", + "behaviour, they eventually adjusted to the decentralisation.", + "", + "Eventually, PSI users grew so adept at manipulating the", + "diluted flows of PSI that they were capable of the same things", + "as before. Each PSI user would keep their own individual", + "reserves of PSI for when they needed to weild greater power,", + "and the balance of power was restored." }; + +char *STextV[15] = {"Merit decided to assume the role of custodian over the Orcus", + "Dome, in Wervyn Anixil's place. He resumed the experiments on", + "PSI and found ways of making the Dome's remaining supply go as", + "far as it could.", + "", + "Other PSI users were suspicious of MERIT, just as they were", + "wary of Wervyn Anixil before him, but they soon adjusted.", + "", + "The balance of power was quickly restored, and stabilised for", + "eternity due to the work of Wervyn Anixil and now MERIT.", + "", + "", + " [[ BEST ENDING ]]", + "", + ""}; + +void DrawSText(int t) +{ + int offset = 540 + (t / 2); + int i, c; + t = t * 350 / 500; + int cl = 350 - t; + + for (i = 0; i < 64; i++) { + DrawRect(0, i * 15 - offset, 640, 15, (64 - i) * cl / 350); + } + for (i = 64; i < 128; i++) { + DrawRect(0, i * 15 - offset, 640, 15, (i - 64) * cl / 350); + } + + if (t < 300) { + for (i = 0; i < 15; i++) { + c = (255 + (i * 100) - t*10); + if (c < 0) c = 0; + if (c > 255) c = 255; + + draw_text(68, 150+i*12, SText[i], 255-c); + } + } else { + for (i = 0; i < 15; i++) { + c = 5 + (t-300) * 5; + + draw_text(68, 150+i*12, SText[i], 255-c); + } + } + + UpdatePalette(); + VideoUpdate(); +} +void DrawSTextV(int t) +{ + int offset = 540 + (t / 2); + int i, c; + t = t * 350 / 500; + int cl = 350 - t; + + for (i = 0; i < 64; i++) { + DrawRect(0, i * 15 - offset, 640, 15, (64 - i) * cl / 350); + } + for (i = 64; i < 128; i++) { + DrawRect(0, i * 15 - offset, 640, 15, (i - 64) * cl / 350); + } + + if (t < 300) { + for (i = 0; i < 15; i++) { + c = (255 + (i * 100) - t*10); + if (c < 0) c = 0; + if (c > 255) c = 255; + + draw_text(68, 150+i*12, STextV[i], 255-c); + } + } else { + for (i = 0; i < 15; i++) { + c = 5 + (t-300) * 5; + + draw_text(68, 150+i*12, STextV[i], 255-c); + } + } + + UpdatePalette(); + VideoUpdate(); +} + + +float pt_x[500]; +float pt_y[500]; +float pt_vx[500]; +float pt_vy[500]; +int pt_t[500]; + +void InitParticleStorm() +{ + int i; + + for (i = 0; i < 500; i++) { + pt_x[i] = 320; + pt_y[i] = 960; + pt_vx[i] = (float)(rand()%101) / 33.333 - 1.5; + pt_vy[i] = (float)(rand()%101) / 10.0 - 16.1; + pt_t[i] = rand()%100; + } +} + +char *credits[] = { + "Concept: Lancer-X/Asceai", + "Game design: Lancer-X/Asceai", + "Graphics: Lancer-X/Asceai", + "Programming: Lancer-X/Asceai", + "Sound Effects: Various (public domain) sources", + "Music: Various artists", + "Beta testing: Quasar", + "Beta testing: Terryn", + "Beta testing: Wervyn" + "\"Ambient Light\" Vogue of Triton" + "\"Battle of Ragnarok\" Frostbite" + "\"Dragon Cave\" TICAZ" + " cavern.xm Unknown" + "\"Caverns Boss\" Alexis Janson" + "\"Forest Boss\" Alexis Janson" + "\"Catacombs Boss\" Alexis Janson" + "\"Fear 2\" Mick Rippon" + "\"The Final Battle\" Goose/CéDA & iNVASiON" + "\"Ice Frontier\" Skaven/FC" + "\"KnarkLoader 1.0\" Rapacious" + "\"RPG-Battle\" Cyn" + "\"Metallic Forest\" Joseph Fox" +}; + +void DrawCredits() +{ + static SDL_Surface *fin = NULL; + static SDL_Surface *theend[2] = {NULL}; + SDL_Rect draw_to; + int i; + int ypos; + int c; + int n_credits = sizeof(credits)/sizeof(*credits); + int finish_point; + + finish_point = 400 + (n_credits * 50); + + draw_to.x = 384; + draw_to.y = 352; + + SDL_FillRect(screen, NULL, 0); + + if (fin == NULL) { + fin = IMG_Load("dat/i/fin.png"); + + theend[0] = IMG_Load("dat/i/theend.png"); + theend[1] = IMG_Load("dat/i/true_end.png"); + } + + if (credits_scroll >= (finish_point + 80)) { + SDL_BlitSurface(theend[(player_shield == 30)], NULL, screen, NULL); + } else { + SDL_BlitSurface(fin, NULL, screen, &draw_to); + + // Show each line of credits + + for (i = 0; i < n_credits; i++) { + ypos = 800 + (i * 100) - credits_scroll * 2; + + if ((ypos >= 0)&&(ypos < 480)) { + c = 255 - abs(ypos - 240); + draw_text(120, ypos, credits[i], c); + } + } + + } + + for (i = 0; i < 128; i++) { + ending_pal[i].r = 0; + ending_pal[i].g = i; + ending_pal[i].b = i*2; + } + for (i = 128; i < 256; i++) { + ending_pal[i].r = (i - 128)*2+1; + ending_pal[i].g = i; + ending_pal[i].b = 255; + } + + // Dim palette if we're just starting + + if (credits_scroll < 80) { + for (i = 0; i < 256; i++) { + ending_pal[i].r = ending_pal[i].r * credits_scroll / 80; + ending_pal[i].g = ending_pal[i].g * credits_scroll / 80; + ending_pal[i].b = ending_pal[i].b * credits_scroll / 80; + } + } + + // Also palette if we're finishing + + if ((credits_scroll >= (finish_point))&&(credits_scroll < (finish_point + 80))) { + for (i = 0; i < 256; i++) { + ending_pal[i].r = ending_pal[i].r * (finish_point+80-credits_scroll) / 80; + ending_pal[i].g = ending_pal[i].g * (finish_point+80-credits_scroll) / 80; + ending_pal[i].b = ending_pal[i].b * (finish_point+80-credits_scroll) / 80; + } + } + + if ((credits_scroll >= (finish_point + 80))&&(credits_scroll < (finish_point + 160))) { + for (i = 0; i < 256; i++) { + ending_pal[i].r = ending_pal[i].r * (credits_scroll - (finish_point + 80)) / 80; + ending_pal[i].g = ending_pal[i].g * (credits_scroll - (finish_point + 80)) / 80; + ending_pal[i].b = ending_pal[i].b * (credits_scroll - (finish_point + 80)) / 80; + } + } + + credits_scroll++; + + UpdatePalette(); + VideoUpdate(); +} + +void RunParticleStorm(int offset) +{ + SDL_Rect draw_from, draw_to; + int i; + + for (i = 0; i < 64; i++) { + DrawRect(0, i * 15 - offset, 640, 15, 64 - i); + } + + for (i = 0; i < 500; i++) { + if (pt_t[i] > 0) { + pt_t[i]--; + } else { + pt_vy[i] += 0.1; + pt_x[i] += pt_vx[i]; + pt_y[i] += pt_vy[i]; + } + + draw_from.x = (rand()%3)*32; + draw_from.y = 0; + draw_from.w = 32; + draw_from.h = 32; + + draw_to.x = (int)pt_x[i] - 16; + draw_to.y = (int)pt_y[i] - 16 - offset; + SDL_BlitSurface(glitter, &draw_from, screen, &draw_to); + } + + for (i = 0; i < 128; i++) { + ending_pal[i].r = i*2; + ending_pal[i].g = i*2; + ending_pal[i].b = 0; + } + for (i = 128; i < 256; i++) { + ending_pal[i].r = 255; + ending_pal[i].g = 255; + ending_pal[i].b = (i - 128)*2+1; + } + + UpdatePalette(); + VideoUpdate(); +} + +void DrawStream(int t) +{ + int i; + int scr_x = 32; + int scr_y = 0; + int strm_scrl; + SDL_Rect draw_from, draw_to; + + for (i = 0; i < 256; i++) { + ending_pal[i].r = i; + ending_pal[i].g = (i * 7 / 8) + 16 + sin( (float)t / 8 )*16; + ending_pal[i].b = (i * 3 / 4) + 32 + sin( (float)t / 8 )*32; + } + + + if (t >= 300) { + scr_x = 32 + rand()%32 - rand()%32; + scr_y = rand()%8; + } + + if (t < 10) { + scr_y = (20 - t * 2); + } + + DrawLevel(scr_x, scr_y, 0, 0); + DrawPlayer(344 - scr_x, 228 - scr_y, 0, 0); + + for (i = 0; i < 7; i++) { + strm_scrl = (t * 20) % 128; + draw_to.x = 0 - strm_scrl - scr_x + (128*i); + draw_to.y = 19 - scr_y; + + if (i >= 300) { + draw_to.y += rand()%4; + draw_to.y -= rand()%4; + } + SDL_BlitSurface(streamspr, NULL, screen, &draw_to); + } + + // glitter + for (i = 0; i < 20; i++) { + draw_from.x = (rand()%3)*32; + draw_from.y = 0; + draw_from.w = 32; + draw_from.h = 32; + + draw_to.x = rand()%(640+32)-32; + draw_to.y = (rand()%(124)) + 3; + + SDL_BlitSurface(glitter, &draw_from, screen, &draw_to); + } + + if (t > 250) { + if (t < 300) { + if (t == 251) { + SND_CircuitRelease(1000); + } + DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 254) * 10, 255); + DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 252) * 10, 225); + DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 250) * 10, 195); + } + } + + UpdatePalette(); + VideoUpdate(); +} + +char *PText[10] = { "Activating the seal quickly unblocked the ley lines and allowed", + "PSI to flow through the Dome again. The remaining shadows were", + "quickly flushed out.", + "", + "Wervyn Anixil's unconventional use of the PSI resulted in him", + "being burned out and rendered powerless. Merit will see to it", + "that he faces judgement for his crimes.", + "", + "Neither of the two PSI weapons housed within the Dome had been", + "touched. However . . ." }; +char *PTextV[10] ={ "Activating the seal quickly unblocked the ley lines and allowed", + "PSI to flow through the Dome again. The remaining shadows were", + "quickly flushed out.", + "", + "The traitor, who was never identified, perished in the Sealing.", + "It soon became clear that the traitor had managed to betray and", + "kill the real Wervyn Anixil during his experiments on the PSI.", + "If the Agate Knife was never found, nobody would have been any", + "the wiser, and things could have turned out very differently.", + "However, there was one last thing for MERIT to do."}; + +void DrawPText(int t) +{ + int i; + int c; + + int x, y; + + for (i = 0; i < 256; i++) { + ending_pal[i].r = i; + ending_pal[i].g = i; + ending_pal[i].b = (i * 3 / 4) + 64; + } + + if (t < 300) { + + for (i = 0; i < 10; i++) { + c = (255 + (i * 100) - t*10); + if (c < 0) c = 0; + if (c > 255) c = 255; + + if (player_shield != 30) { + draw_text(68, 180+i*12, PText[i], c); + } else { + draw_text(68, 180+i*12, PTextV[i], c); + } + } + } else { + for (i = 0; i < 10; i++) { + c = 5 + (t-300) * 5; + + if (player_shield != 30) { + draw_text(68, 180+i*12, PText[i], c); + } else { + draw_text(68, 180+i*12, PTextV[i], c); + } + } + } + + for (i = 0; i < (32 * 8); i++) { + x = (i % 32)*20; + y = (i / 32)*20; + + c = 237 + (i/32*2) + (rand()% (19 - (i/32) *2)); + DrawRect(x, y, 20, 20, c); + c = 237 + (i/32*2) + (rand()% (19 - (i/32) *2)); + DrawRect(x, 460 - y, 20, 20, c); + } + + + UpdatePalette(); + VideoUpdate(); +} + +void DrawScrolly(int t) +{ + int xp; + int yp; + int i, j; + float a_dir; + float v_radius; + int all_blue = 0; + SDL_Rect draw_from, draw_to; + + int x, y, r; + + float bright; + + if (t < 795) { + xp = 8192 - 320 - 3180 + (t * 4); + yp = t * 20; + } else { + xp = 8192 - 320 + ( (t-795) * 10); + yp = 795 * 20 - (t-795)*10; + } + + // Palette + + + if ((rand() % 10)==9) { + all_blue = 1; + } + for (i = 0; i < 256; i++) { + bright = sin((float)t / 10.0) * 0.2 + 0.4; + ending_pal[i].r = (i * bright + (256*(1.0-bright))) * ((float)(all_blue == 0) * 0.5 + 0.5); + ending_pal[i].g = (i * bright + (256*(1.0-bright))) * ((float)(all_blue == 0) * 0.5 + 0.5); + ending_pal[i].b = i * bright + (256*(1.0-bright)); + } + DrawLevel(xp, yp, 0, 0); + + v_radius = sin((float)t / 10.0)*20 + 100; + + for (i = 0; i < 5; i++) { + x = rand()%640; + y = rand()%480; + r = rand()%500+100; + + DrawCircleEx(x, y, r+2, r-4, 128); + DrawCircleEx(x, y, r, r-2, 255); + } + + for (i = 0; i < 4; i++) { + draw_from.x = (8 + i) * 32; + draw_from.y = 0; + draw_from.w = 32; + draw_from.h = 32; + + a_dir = ((float)t / 10.0) + (M_PI*(float)i/2); + + for (j = 10; j >= 0; j--) { + DrawCircleEx(320+cos(a_dir)*v_radius, 240+sin(a_dir)*v_radius, 22 + j * 2, 0, abs(j-3) * 15); + } + DrawCircleEx(320+cos(a_dir)*v_radius, 240+sin(a_dir)*v_radius, 20, 0, 0); + + draw_to.x = 320 + cos(a_dir) * v_radius - 16; + draw_to.y = 240 + sin(a_dir) * v_radius - 16; + SDL_BlitSurface(artifact_spr, &draw_from, screen, &draw_to); + } + + UpdatePalette(); + VideoUpdate(); +} + +void DrawCircuitFlash(int t, int method) +{ + static SDL_Surface *circ = NULL; + static int xpos, ypos; + int i, j; + SDL_Rect from; + + if (circ == NULL) { + circ = IMG_Load("dat/i/circuits_1.png"); + } + + if (t == 0) { + if (method == 0) { + xpos = rand()%641; + ypos = rand()%481; + } else { + xpos = 320; + ypos = 240; + } + } + + from.x = xpos; + from.y = ypos; + from.w = 640; + from.h = 480; + + SDL_BlitSurface(circ, &from, screen, NULL); + + for (i = 0; i < 256; i++) { + if (method == 0) { + j = i * t / 4; + } else { + j = i * t / 8; + if (t >= 20) { + j += t * 25; + } + } + + if (j > 255) j = 255; + ending_pal[i].r = j; + ending_pal[i].g = j; + ending_pal[i].b = j; + } + + UpdatePalette(); + VideoUpdate(); +} diff --git a/src/ending.h b/src/ending.h new file mode 100755 index 0000000..6130888 --- /dev/null +++ b/src/ending.h @@ -0,0 +1,29 @@ +// +// ending.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// Exposes ending.c functionality and types + +#ifndef ENDING_H +#define ENDING_H + +void ShowEnding(); + +#endif diff --git a/src/gamemap.c b/src/gamemap.c new file mode 100755 index 0000000..7c13bed --- /dev/null +++ b/src/gamemap.c @@ -0,0 +1,342 @@ +// +// gamemap.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include + +#include "levelblit.h" +#include "mapgen.h" +#include "tiles.h" +#include "save.h" + +SDL_Surface *automap = NULL; +void RecordRoom(int room_id); +int full_rend = 0; +void FullRender(); +int c_scroll_x=0, c_scroll_y=0; +SDL_Surface *overview = NULL; + +void InitAutomap() +{ + int i; + if (automap != NULL) SDL_FreeSurface(automap); + if (overview != NULL) SDL_FreeSurface(overview); + + automap = IMG_Load("dat/i/automap.png"); //520x520 + overview = IMG_Load("dat/i/overview.png"); //200x200 + + //SDL_SetColorKey(automap, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255); + + full_rend = 0; + if (game_load) { + if (artifacts[0]) { + FullRender(); + } else { + for (i = 0; i < 3000; i++) { + if (rooms[i].visited) { + RecordRoom(i); + } + } + RecordRoom(player_room); + } + } +} + +void FullRender() +{ + int x, y, i; + Uint8 *pix; + + for (y = 0; y < map.h; y++) { + for (x = 0; x < map.w; x++) { + if (TileData[Get(x, y)].Is_Solid) { + if (Get(x, y) != 17) { + pix = automap->pixels; + pix += automap->w * (y + 4); + pix += x + 4; + *pix = 128; + } + } + } + } + + for (i = 0; i < 3000; i++) { + if (rooms[i].visited) { + RecordRoom(i); + } + } + RecordRoom(player_room); + + full_rend = 1; +} + +void DisplayRoom(int room_id, Uint8 room_bg) +{ + int x, y; + int rx, ry; + SDL_Rect fill; + Uint8 *pix; + + for (y = 0; y < rooms[room_id].h; y++) { + for (x = 0; x < rooms[room_id].w; x++) { + rx = x + rooms[room_id].x; + ry = y + rooms[room_id].y; + + pix = automap->pixels; + pix += automap->w * (ry + 4); + pix += rx + 4; + + *pix = automap_cols[TileData[Get(rx, ry)].Type]; + if (*pix == 192) *pix = room_bg; + } + } + + fill.x = rooms[room_id].x * 200 / 512; + fill.y = rooms[room_id].y * 200 / 512; + fill.w = rooms[room_id].w * 200 / 512; + fill.h = rooms[room_id].h * 200 / 512; + + SDL_FillRect(overview, &fill, 200); +} + +void RecordRoom(int room_id) +{ + static int last_player_room = -1; + if (last_player_room != -1) { + if (rooms[last_player_room].checkpoint) DisplayRoom(last_player_room, 150 - (rooms[last_player_room].s_dist/5*12)); + else DisplayRoom(last_player_room, 192 - (rooms[last_player_room].s_dist/5*12)); + } + last_player_room = room_id; + DisplayRoom(room_id, 0); +} + +void DisplayAutomap() +{ + SDL_Rect from; + SDL_Rect position; + int x, y; + int df_x, df_y; + int rx, ry; + int rw, rh; + int tile; + int nearest_checkpoint; + unsigned char col; + unsigned char xcol = 0; + static int t = 0; + int minimap_scroll_x, minimap_scroll_y; + + if (key_held[K_UP]) { + c_scroll_y -= 32 + (key_held[K_SP]*64); + } + if (key_held[K_DN]) { + c_scroll_y += 32 + (key_held[K_SP]*64); + } + if (key_held[K_LT]) { + c_scroll_x -= 32 + (key_held[K_SP]*64); + } + if (key_held[K_RT]) { + c_scroll_x += 32 + (key_held[K_SP]*64); + } + + if (c_scroll_x < 0) c_scroll_x = 0; + if (c_scroll_y < 0) c_scroll_y = 0; + if (c_scroll_x >= 512*32) c_scroll_x = 512*32 - 32; + if (c_scroll_y >= 512*32) c_scroll_y = 512*32 - 32; + + nearest_checkpoint = GetNearestCheckpoint(c_scroll_x, c_scroll_y); + + if (artifacts[0] && (!full_rend)) FullRender(); + + minimap_scroll_x = c_scroll_x / 32 - 200 / 2; + minimap_scroll_y = c_scroll_y / 32 - 229 / 2; + if (minimap_scroll_x < 0) minimap_scroll_x = 0; + if (minimap_scroll_y < 0) minimap_scroll_y = 0; + + if (minimap_scroll_x >= 512 - 200) minimap_scroll_x = 512 - 200 - 1; + if (minimap_scroll_y >= 512 - 229) minimap_scroll_y = 512 - 229 - 1; + + position.x = 437; + position.y = 235; + position.w = 200; + position.h = 229; + + from.x = minimap_scroll_x + 4; + from.y = minimap_scroll_y + 4; + from.w = 200; + from.h = 229; + + SDL_FillRect(screen, &position, 255); + SDL_BlitSurface(automap, &from, screen, &position); + + rx = c_scroll_x / 32 - 27 - minimap_scroll_x + 437; + ry = c_scroll_y / 32 - 27 - minimap_scroll_y + 235; + + DrawRect(rx, ry, 55, 1, 50); + DrawRect(rx, ry+54, 55, 1, 50); + DrawRect(rx, ry, 1, 55, 50); + DrawRect(rx+54, ry, 1, 55, 50); + + from.x = 0; + from.y = 0; + from.w = 200; + from.h = 200; + + position.x = 437; + position.y = 32; + + SDL_BlitSurface(overview, &from, screen, &position); + + rx = (c_scroll_x / 32 - 27) * 200 / 512 + 437; + ry = (c_scroll_y / 32 - 27) * 200 / 512 + 32; + rw = 54 * 200 / 512; + rh = 54 * 200 / 512; + + if (rx < 437) { + rw -= (437 - rx); + rx = 437; + } + if (ry < 32) { + rh -= (32 - ry); + ry = 32; + } + if (rx+rw >= 437 + 200 - 1) { + rw -= ((rx+rw) - (437 + 200 - 1)); + } + if (ry+rh >= 32 + 200 - 1) { + rh -= ((ry+rh) - (32 + 200 - 1)); + } + + DrawRect(rx, ry, rw + 1, 1, 50); + DrawRect(rx, ry + rh, rw + 1, 1, 50); + DrawRect(rx, ry, 1, rh + 1, 50); + DrawRect(rx + rw, ry, 1, rh + 1, 50); + + DrawRect(2, 31, 432, 432, 255); + t++; + for (y = 0; y < 54; y++) { + for (x = 0; x < 54; x++) { + xcol = 0; + df_x = x * 8 + 2; + df_y = y * 8 + 32; + + rx = c_scroll_x / 32 - 27 + x; + ry = c_scroll_y / 32 - 27 + y; + if ((rx >= 0)&&(ry >= 0)&&(rx < 512)&&(ry < 512)) { + if (rooms[GetRoom(rx, ry)].visited) { + tile = Get(rx, ry); + if (tele_select && (nearest_checkpoint == GetRoom(rx, ry)) && ((t / 3) % 2)) { + xcol = 255; + } + col = automap_cols[TileData[tile].Type]; + DrawRect(df_x, df_y, 8, 8, col ^ xcol); + + switch (tile) { + case 25: + DrawRect(df_x, df_y, 8, 8, 192 ^ xcol); + draw_char(df_x, df_y, '*', 0 ^ xcol); + break; + case 26: + DrawRect(df_x, df_y, 8, 8, 192 ^ xcol); + draw_char(df_x, df_y, '+', 0 ^ xcol); + break; + case 28: + DrawRect(df_x, df_y, 8, 8, 192 ^ xcol); + draw_char(df_x, df_y, 'S', 0 ^ xcol); + break; + case 29: + DrawRect(df_x, df_y, 8, 8, 192 ^ xcol); + draw_char(df_x, df_y, 'C', 0 ^ xcol); + break; + case 30: + DrawRect(df_x, df_y, 8, 8, 192 ^ xcol); + draw_char(df_x, df_y, 'R', 0 ^ xcol); + break; + case 31: + DrawRect(df_x, df_y, 8, 8, 192 ^ xcol); + draw_char(df_x, df_y, 'S', 0 ^ xcol); + break; + case 32: + DrawRect(df_x, df_y, 8, 8, 192 ^ xcol); + draw_char(df_x, df_y, 29, 0 ^ xcol); + break; + case 53: + DrawRect(df_x, df_y, 8, 8, 192 ^ xcol); + draw_char(df_x, df_y, 28, 0 ^ xcol); + break; + } + + if (tile < 12) { + DrawRect(df_x, df_y, 8, 8, 0 ^ xcol); + draw_char(df_x, df_y, tile + 10, 255 ^ xcol); + } + + if ((tile >= 45)&&(tile <= 52)) { + DrawRect(df_x, df_y, 8, 8, 0 ^ xcol); + draw_char(df_x, df_y, (tile - 45)%4 + 14, 255 ^ xcol); + } + + if ((tile >= 13)&&(tile < 17)) { + DrawRect(df_x, df_y, 8, 8, 0 ^ xcol); + draw_char(df_x, df_y, tile - 13 + 22, 255 ^ xcol); + } + if ((tile >= 21)&&(tile < 25)) { + DrawRect(df_x, df_y, 8, 8, 0 ^ xcol); + draw_char(df_x, df_y, tile - 13 + 22, 255 ^ xcol); + } + if ((tile >= 38)&&(tile < 42)) { + DrawRect(df_x, df_y, 8, 8, 0 ^ xcol); + draw_char(df_x, df_y, tile - 13 + 22, 255 ^ xcol); + draw_char(df_x, df_y, 26, 255 ^ xcol); + } + + if (( (player_x / 32) == rx)&&( (player_y / 32) == ry)) { + DrawRect(df_x, df_y, 8, 8, 0 ^ xcol); + draw_char(df_x, df_y, 30, 255 ^ xcol); + } + } + } + } + } + + + // Now, to cover up the gaps! + + //DrawRect(0, 29, 640, 437, 230); + + // +-------1----+-----+ + // | | | + // 3 4 5 + // | +--6--+ + // | | | + // | | | + // +-------2----+-----+ + + DrawRect(0, 29, 640, 3, 230); // 1 + DrawRect(0, 464, 640, 4, 230); // 2 + DrawRect(0, 29, 2, 437, 230); // 3 + DrawRect(434, 29, 3, 437, 230); // 4 + DrawRect(637, 29, 3, 437, 230); // 5 + DrawRect(437, 232, 200, 3, 230); // 6 + +} diff --git a/src/gamemap.h b/src/gamemap.h new file mode 100755 index 0000000..a38ad7d --- /dev/null +++ b/src/gamemap.h @@ -0,0 +1,34 @@ +// +// gamemap.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// Exposes gamemap.c functionality and types + +#ifndef GAMEMAP_H +#define GAMEMAP_H + +void RecordRoom(int room_id); +void DisplayAutomap(); +void InitAutomap(); + +extern int c_scroll_x, c_scroll_y; + +#endif + diff --git a/src/help.c b/src/help.c new file mode 100755 index 0000000..cecc3fd --- /dev/null +++ b/src/help.c @@ -0,0 +1,239 @@ +// +// help.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include +#include + +#include "levelblit.h" + +struct help_line { + char *t; +}; + +struct help_section { + int lines; + char *identifier; + struct help_line *l[256]; +}; + +struct help_file { + int sections; + struct help_section *s[256]; +}; + +struct help_file *hlp = NULL; +int my_line; +int my_sec; +int my_cursor; +int my_link; +void InitHelp() +{ + FILE *fp; + struct help_section *current_sec = NULL; + struct help_line *current_line = NULL; + char linebuf[80]; + hlp = malloc(sizeof(struct help_file)); + hlp->sections = 0; + + fp = fopen("dat/d/helpfile.txt", "r"); + while (!feof(fp)) { + fgets(linebuf, 79, fp); + if (linebuf[strlen(linebuf)-1] == '\n') + linebuf[strlen(linebuf)-1] = 0; + + if (linebuf[0] == '\'') { + // comment + continue; + } + if (linebuf[0] == ':') { + // section + hlp->s[hlp->sections] = malloc(sizeof(struct help_section)); + current_sec = hlp->s[hlp->sections]; + hlp->sections++; + current_sec->identifier = malloc(strlen(linebuf)); + current_sec->lines = 0; + strcpy(current_sec->identifier, linebuf+1); + continue; + } + + // different line + if (current_sec != NULL) { + current_sec->l[current_sec->lines] = malloc(sizeof(struct help_line)); + current_line = current_sec->l[current_sec->lines]; + current_sec->lines++; + current_line->t = malloc(strlen(linebuf)+1); + strcpy(current_line->t, linebuf); + } + } + fclose(fp); +} + +void DisplayHelp() +{ + static int tick = 0; + int i; + struct help_section *current_sec = NULL; + char *ltext; + char c_ident[20]; + int line_num; + int follow_link = 0; + char linkfollow[20] = ""; + + DrawRect(23, 23, 594, 434, 0); + DrawRect(24, 24, 592, 432, 200); + DrawRect(25, 25, 590, 430, 255); + DrawRect(26, 26, 588, 428, 200); + DrawRect(27, 27, 586, 426, 100); + DrawRect(30, 30, 580, 420, 20); + DrawRect(35, 35, 570, 410, 60); + + // 70x40 display + current_sec = hlp->s[my_sec]; + + my_line = my_cursor - 19; + if (my_line < 0) my_line = 0; + if (my_line >= (current_sec->lines)) my_line = current_sec->lines - 1; + for (i = 0; i < 2; i++) { + draw_text(23+i, 40+(my_cursor - my_line)*10, "->", 255); + draw_text(599+i, 40+(my_cursor - my_line)*10, "<-", 255); + } + + for (i = 0; i < 40; i++) { + line_num = my_line + i; + if (line_num >= 0) { + if (line_num < current_sec->lines) { + ltext = current_sec->l[line_num]->t; + + switch (ltext[0]) { + case '!': + + draw_text(40 + (560-strlen(ltext+1)*8)/2, 40+i*10, ltext+1, 255); + break; + case '?': + strncpy(c_ident, ltext+1, strchr(ltext+1, '?')-ltext-1); + c_ident[strchr(ltext+1, '?')-ltext-1] = 0; + + draw_text(40, 40+i*10, strchr(ltext+1, '?')+1, my_cursor == line_num ? 200+(tick%16)*3 : 150); + if ((my_link == 1)&&(my_cursor == line_num)) { + follow_link = 1; + strcpy(linkfollow, c_ident); + } + break; + default: + draw_text(40, 40+i*10, ltext, 200); + break; + } + } + } + } + tick++; + SDL_UpdateRect(screen, 0, 0, 0, 0); + + if (follow_link) { + for (i = 0; i < hlp->sections; i++) { + if (strcmp(linkfollow, hlp->s[i]->identifier) == 0) { + my_sec = i; + my_cursor = 0; + break; + } + } + my_link = 0; + } +} + +int MoveCursor() +{ + SDL_Event ev; + static int key_delay = 0; + static int key_up = 0, key_down = 0; + + if (key_delay > 0) key_delay--; + + my_link = 0; + while (SDL_PollEvent(&ev)) { + if (ev.type == SDL_KEYDOWN) { + if (ev.key.keysym.sym == SDLK_DOWN) { + key_down = 1; + key_delay = 10; + if (my_cursor < hlp->s[my_sec]->lines-1) my_cursor++; + } + if (ev.key.keysym.sym == SDLK_UP) { + key_up = 1; + key_delay = 10; + if (my_cursor > 0) my_cursor--; + } + if (ev.key.keysym.sym == SDLK_ESCAPE) { + return 0; + } + if (ev.key.keysym.sym == SDLK_h) { + return 0; + } + if ((ev.key.keysym.sym == SDLK_SPACE) || (ev.key.keysym.sym == SDLK_RETURN)) + my_link = 1; + } + + if (ev.type == SDL_KEYUP) { + if (ev.key.keysym.sym == SDLK_DOWN) { + key_down = 0; + } + if (ev.key.keysym.sym == SDLK_UP) { + key_up = 0; + } + } + if (ev.type == SDL_QUIT) { + return 0; + } + } + + if (key_delay == 0) { + if (key_up == 1) { + if (my_cursor > 0) my_cursor--; + } + if (key_down == 1) { + if (my_cursor < hlp->s[my_sec]->lines-1) my_cursor++; + } + } + + return 1; +} + +void ShowHelp() +{ + int in_help = 1; + if (hlp == NULL) { + InitHelp(); + } + my_line = 0; + my_sec = 0; + my_cursor = 0; + my_link = 0; + + while (in_help) + { + DisplayHelp(); + in_help = MoveCursor(); + SDL_Delay(30); + } +} diff --git a/src/help.h b/src/help.h new file mode 100755 index 0000000..35c6050 --- /dev/null +++ b/src/help.h @@ -0,0 +1,29 @@ +// +// help.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// help.c header file + +#ifndef HELP_H +#define HELP_H + +void ShowHelp(); + +#endif diff --git a/src/levelblit.c b/src/levelblit.c new file mode 100755 index 0000000..edd3a35 --- /dev/null +++ b/src/levelblit.c @@ -0,0 +1,2646 @@ +// +// levelblit.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mapgen.h" +#include "demon.h" +#include "gamemap.h" +#include "tiles.h" +#include "save.h" +#include "help.h" +#include "audio.h" +#include "boss.h" +#include "ending.h" + +#define PLAYERW 16 +#define PLAYERH 24 + +#define MERITOUS_VERSION "v 1.1" +int RECORDING = 0; +int PLAYBACK = 0; + +int expired_ms = 0; +int frame_len = 33; +int WriteBitmaps = 0; +int WB_StartRange = 0; +int WB_EndRange = 1000000; +int training = 0; +int game_paused = 0; +int show_ending = 0; +int voluntary_exit = 0; +int tele_select = 0; +int enter_room_x = 0, enter_room_y = 0; + +int agate_knife_loc = -1; + +FILE *record_file; +char record_filename[256]; + +void DrawLevel(int off_x, int off_y, int hide_not_visited, int fog_of_war); +void DrawPlayer(int x, int y, int pl_dir, int pl_frm); +void LoadLevel(); +void ActivateRoom(int room); + +void DrawCircuit(); +void ReleaseCircuit(); +void DrawCircle(int x, int y, int r, unsigned char c); + +void DrawArtifacts(); + +void HandleEvents(); + +void text_init(); +void draw_text(int x, int y, char *str, Uint8 tcol); +unsigned char font_data[128][8][8]; + +void DrawShield(); + +int key_held[10] = {0}; +int game_running = 1; + +int player_x; +int player_y; +int player_dying; +int magic_circuit; +int circuit_range; +int release_range; +int release_x; +int release_y; +int release_str; + +int shield_hp; +int shield_recover; +int player_gems; +int checkpoints_found; +int circuit_size; +int first_game; +int player_hp; +int player_lives = 5; +int player_lives_part = 0; + +int player_room; +int player_dir; +int player_wlk; +int player_walk_speed; +int wlk_wait; +int circuit_release; +int scroll_home; +int enter_pressed; + +int opening_door_x, opening_door_y, opening_door_i = 0, opening_door_n; + +int checkpoint_x; +int checkpoint_y; + +int explored = 0; +//#define DEBUG_STATS 1 + +int artifacts[12]; +SDL_Surface *artifact_spr = NULL; + +int player_shield; +int circuit_fillrate; +int circuit_recoverrate; + +int scroll_x, scroll_y; + +int map_enabled; + +int prv_player_room; + +int specialmessage; +int specialmessagetimer; + +int timer_ps = 0; +int timer_v[10]; + +float RandomDir() +{ + return (float)(rand()%256)*M_PI*2.0/256.0; +} + +int UpgradePrice(int t); + +void PlayerDefaultStats() +{ + int i; + + player_dying = 0; + magic_circuit = 0; + circuit_range = 100; + release_range = 100; + shield_hp = 0; + shield_recover = 0; + player_gems = 0; + checkpoints_found = 0; + circuit_size = 1000; + first_game = 1; + player_hp = 3; + + explored = 0; + + voluntary_exit = 0; + player_room = 0; + player_dir = 0; + player_wlk = 0; + player_walk_speed = 5; + player_lives = 5; + player_lives_part = 0; + wlk_wait = 8; + circuit_release = 0; + scroll_home = 0; + enter_pressed = 0; + show_ending = 0; + + game_paused = 0; + + player_shield = 0; + circuit_fillrate = 2; + circuit_recoverrate = 3; + + prv_player_room = -1; + + specialmessage = 0; + specialmessagetimer = 0; + + opening_door_i = 0; + + map_enabled = 0; + + for (i = 0; i < 12; i++) { + artifacts[i] = 0; + } + + #ifdef DEBUG_STATS + + player_shield = 24; + circuit_fillrate = 24; + circuit_recoverrate = 24; + + for (i = 0; i < 12; i++) { + artifacts[i] = 1; + } + + #endif +} + + +void ScrollTo(int x, int y); +#define K_UP 0 +#define K_DN 1 +#define K_LT 2 +#define K_RT 3 +#define K_SP 4 + +SDL_Surface *screen; + +void SetGreyscalePalette(); +void SetTonedPalette(float pct); +void SetTitlePalette(int curve_start, int curve_end); +void SetTitlePalette2(int t); +int TouchTile(int ix, int iy); +void SpecialTile(int x, int y); +void DrawRect(int x, int y, int w, int h, unsigned char c); + +void DrawCircleEx(int x, int y, int r, int r2, unsigned char c); + +void ThinLine(SDL_Surface *scr, int x1, int y1, int x2, int y2, Uint8 col); +void LockDoors(int r); + +#define SCREEN_W 640 +#define SCREEN_H 480 + +void VideoUpdate() +{ + static int bmp = 0; + char bmp_name[256]; + + SDL_UpdateRect(screen, 0, 0, 0, 0); + if (WriteBitmaps) { + if ((bmp >= WB_StartRange)&&(bmp < WB_EndRange)) { + sprintf(bmp_name, "v/bmp%d.bmp", bmp); + SDL_SaveBMP(screen, bmp_name); + } + bmp++; + } +} + +void EndCycle(int n) +{ + static int last_ticks; + int tick_delta; + tick_delta = SDL_GetTicks() - last_ticks; + + if (n == 0) n = frame_len; + + if (tick_delta < n) { + SDL_Delay(n-tick_delta); + } + + if (!game_paused) expired_ms += n; + + last_ticks = SDL_GetTicks(); +} + +void WritePlayerData() +{ + int i; + + FWInt(expired_ms); + FWInt(checkpoint_x); + FWInt(checkpoint_y); + FWInt(scroll_x); + FWInt(scroll_y); + FWInt(magic_circuit); + FWInt(checkpoint_x); + FWInt(checkpoint_y); + FWInt(player_walk_speed); + FWInt(wlk_wait); + FWInt(circuit_fillrate); + FWInt(circuit_recoverrate); + FWInt(explored); + FWInt(player_shield); + FWInt(shield_recover); + FWInt(shield_hp); + FWInt(player_gems); + FWInt(checkpoints_found); + FWInt(player_hp); + FWInt(player_lives); + FWInt(player_lives_part); + FWInt(current_boss); + FWInt(training); + FWInt(agate_knife_loc); + + for (i = 0; i < 12; i++) { + FWChar(artifacts[i]); + } +} + +void ReadPlayerData() +{ + int i; + + expired_ms = FRInt(); + player_x = FRInt(); + player_y = FRInt(); + scroll_x = FRInt(); + scroll_y = FRInt(); + magic_circuit = FRInt(); + checkpoint_x = FRInt(); + checkpoint_y = FRInt(); + player_walk_speed = FRInt(); + wlk_wait = FRInt(); + circuit_fillrate = FRInt(); + circuit_recoverrate = FRInt(); + explored = FRInt(); + player_shield = FRInt(); + shield_recover = FRInt(); + shield_hp = FRInt(); + player_gems = FRInt(); + checkpoints_found = FRInt(); + player_hp = FRInt(); + player_lives = FRInt(); + player_lives_part = FRInt(); + current_boss = FRInt(); + training = FRInt(); + + agate_knife_loc = FRInt(); + + for (i = 0; i < 12; i++) { + artifacts[i] = FRChar(); + } +} + +int min(int x, int y) +{ + if (x255) return 255; + return c; +} + +int dist(int x1, int y1, int x2, int y2) +{ + int dx, dy; + dx = x2 - x1; + dy = y2 - y1; + + return sqrt((dx*dx)+(dy*dy)); +} + +void ClearInput() +{ + key_held[K_SP] = 0; + key_held[K_UP] = 0; + key_held[K_DN] = 0; + key_held[K_LT] = 0; + key_held[K_RT] = 0; +} + +int main(int argc, char **argv) +{ + int on_title = 1; + int executable_running = 1; + SDL_Surface *title, *title_pr, *asceai; + SDL_Surface *wm_icon; + Uint8 *src_p, *col_p; + Uint8 wm_mask[128]; + int i; + int light = 0; + int x, y; + int pulse[SCREEN_W * SCREEN_H]; + int precalc_sine[400]; + int tick = 10000000; + int option = 0; + int can_continue = 0; + int maxoptions; + + int last_key = 0; + + int fullscreen = 0; + int ticker_tick = 0; + unsigned int stime = 0; + + FILE *wm_mask_file; + + if (argc > 1) { + for (i = 1; i < argc; i++) { + if (!strcasecmp(argv[i], "fullscreen")) { + fullscreen = 1; + } +/* if (!strcasecmp(argv[i], "record")) { + RECORDING = 1; + strcpy(record_filename, argv[i+1]); + } + if (!strcasecmp(argv[i], "play")) { + PLAYBACK = 1; + strcpy(record_filename, argv[i+1]); + } + if (!strcasecmp(argv[i], "framedelay")) { + frame_len = atoi(argv[i+1]); + } + if (!strcasecmp(argv[i], "bmpwrite")) { + WriteBitmaps = 1; + } + if (!strcasecmp(argv[i], "bmpstart")) { + WB_StartRange = atoi(argv[i+1]); + } + if (!strcasecmp(argv[i], "bmpend")) { + WB_EndRange = atoi(argv[i+1]); + } */ + } + } + + if ((RECORDING) && (PLAYBACK)) { + exit(1); + } + srand(time(NULL)); + if (RECORDING) { + record_file = fopen(record_filename, "wb"); + stime = time(NULL); + + fputc(stime & 0x000000FF, record_file); + fputc((stime & 0x0000FF00) >> 8, record_file); + fputc((stime & 0x00FF0000) >> 16, record_file); + fputc((stime & 0xFF000000) >> 24, record_file); + + srand(stime); + } + if (PLAYBACK) { + record_file = fopen(record_filename, "rb"); + stime = fgetc(record_file); + stime |= fgetc(record_file) << 8; + stime |= fgetc(record_file) << 16; + stime |= fgetc(record_file) << 24; + + srand(stime); + } + + asceai = IMG_Load("dat/i/asceai.png"); + wm_icon = IMG_Load("dat/i/icon.png"); + + screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 8, SDL_SWSURFACE | (SDL_FULLSCREEN * fullscreen)); + + wm_mask_file = fopen("dat/d/icon_bitmask.dat", "rb"); + fread(wm_mask, 1, 128, wm_mask_file); + fclose(wm_mask_file); + SDL_WM_SetCaption("~ m e r i t o u s ~", "MT"); + SDL_WM_SetIcon(wm_icon, wm_mask); + InitAudio(); + + text_init(); + for (i = 0; i < 400; i++) { + precalc_sine[i] = sin((float)i / 400 * M_PI * 2)*24+24; + } + + + for (i = 0; i < screen->w * screen->h; i++) { + x = i % SCREEN_W; + y = i / SCREEN_W; + + pulse[i] = dist(x, y, SCREEN_W / 2, SCREEN_H / 2); + } + SetGreyscalePalette(); + + // asceai logo + SDL_BlitSurface(asceai, NULL, screen, NULL); + + for (i = 0; i < 75; i++) { + SetTitlePalette(i * 5 - 375, i * 5 - 120); + VideoUpdate(); + DummyEventPoll(); + EndCycle(20); + } + SDL_Delay(500); + for (i = 0; i < 50; i++) { + SetTitlePalette(i * 5, 255 - (i * 5)); + VideoUpdate(); + DummyEventPoll(); + EndCycle(20); + } + SDL_Delay(500); + for (i = 0; i < 50; i++) { + SetTitlePalette(255, (i * 5)+5); + VideoUpdate(); + DummyEventPoll(); + EndCycle(20); + } + + while (executable_running) { + ticker_tick = 0; + TitleScreenMusic(); + + if (IsSaveFile()) { + can_continue = 1; + } else { + can_continue = 0; + } + + maxoptions = 2 + can_continue; + + title = IMG_Load("dat/i/title.png"); + title_pr = IMG_Load("dat/i/title.png"); + + while (on_title) { + SetTitlePalette2(ticker_tick); + col_p = (Uint8 *)title_pr->pixels; + src_p = (Uint8 *)title->pixels; + if ((tick % 10) == 0) { + for (i = 0; i < 640*480; i++) { + *(col_p++) = Uint8_Bound(*(src_p++)+precalc_sine[(pulse[i]+tick)%400]); + } + } + SDL_BlitSurface(title_pr, NULL, screen, NULL); + + draw_text(17, 156, MERITOUS_VERSION, 225 + sin((float)ticker_tick / 15)*30); + if (can_continue) draw_text((SCREEN_W - 14*8)/2, 310, "Continue", 255); + draw_text((SCREEN_W - 14*8)/2, 310 + can_continue*10, "New Game", 255); + draw_text((SCREEN_W - 14*8)/2, 320 + can_continue*10, "New Game (Wuss mode)", 255); + + if (ticker_tick >= 30) { + draw_text((SCREEN_W - 14*8)/2 - 17, 310 + option * 10, "-", 205 + sin((float)ticker_tick / 5.0)*24); + draw_text((SCREEN_W - 14*8)/2 - 20, 310 + option * 10, " >", 205 + sin((float)ticker_tick / 5.0)*24); + draw_text((SCREEN_W - 14*8)/2 - 19, 310 + option * 10, " >", 190 + sin((float)ticker_tick / 5.0)*24); + draw_text((SCREEN_W - 14*8)/2 - 21, 310 + option * 10, " >", 190 + sin((float)ticker_tick / 5.0)*24); + draw_text((SCREEN_W - 14*8)/2 - 18, 310 + option * 10, " >", 165 + sin((float)ticker_tick / 5.0)*24); + draw_text((SCREEN_W - 14*8)/2 - 22, 310 + option * 10, " >", 165 + sin((float)ticker_tick / 5.0)*24); + } + + VideoUpdate(); + + if (ticker_tick++ > 30) { + HandleEvents(); + + if (key_held[K_UP]) { + if (last_key != 1) + if (option > 0) option--; + last_key = 1; + } else { + if (key_held[K_DN]) { + if (last_key != 2) + if (option < maxoptions-1) option++; + last_key = 2; + } else { + last_key = 0; + if (key_held[K_SP] || enter_pressed) { + on_title = 0; + } + } + } + + if (voluntary_exit) { + executable_running = 0; + on_title = 0; + SDL_Quit(); + exit(0); + } + } + + EndCycle(10); + + light = 0; + tick -= 2; + } + + ClearInput(); + + if (executable_running == 1) { + SDL_FreeSurface(title); + SDL_FreeSurface(title_pr); + if ((option == 0) && can_continue) { + DungeonPlay("SaveFile.sav"); + } else { + if (option == (0 + can_continue)) { + training = 0; + DungeonPlay(""); + } else { + training = 1; + DungeonPlay(""); + } + } + // clean up + ClearInput(); + DestroyDungeon(); + DestroyThings(); + on_title = 1; + game_load = 0; + + game_running = 1; + } + } + +// if (argc >= 2) DungeonPlay(argv[1]); +// else DungeonPlay(""); + + SDL_Quit(); + return 0; +} + +void DrawMeter(int x, int y, int n) +{ + static SDL_Surface *meter = NULL; + SDL_Rect drawfrom, drawto; + if (meter == NULL) { + meter = IMG_Load("dat/i/meter.png"); + SDL_SetColorKey(meter, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + } + + drawfrom.x = 0; + drawfrom.y = 6; + drawfrom.w = 150; + drawfrom.h = 6; + + drawto.x = x; + drawto.y = y; + + SDL_BlitSurface(meter, &drawfrom, screen, &drawto); + + drawfrom.w = n*6; + drawfrom.y = 0; + + SDL_BlitSurface(meter, &drawfrom, screen, &drawto); +} + +void ProgressBarScreen(int part, float progress, char *message, float t_parts) +{ + memset(screen->pixels, 0, 640*480); + + DrawRect(200, 217, 240, 50, 80); + DrawRect(202, 219, 236, 46, 20); + draw_text(232, 228, message, 255); + DrawRect(232, 244, 176, 12, 128); + DrawRect(234, 246, 172, 8, 0); + + if ((int)(172.0 * progress / t_parts + (172.0 / t_parts * part)) > 0) { + DrawRect(234, 246, (int)(172.0 * progress / t_parts + (172.0 / t_parts * part)), 8, 200); + } + VideoUpdate(); + DummyEventPoll(); +} + +void LoadingScreen(int part, float progress) +{ + float t_parts; + + if (game_load) t_parts = 5.0; + else t_parts = 3.0; + + ProgressBarScreen(part, progress, "Loading... please wait", t_parts); + ClearInput(); +} + +void SavingScreen(int part, float progress) +{ + ProgressBarScreen(part, progress, "Saving... please wait", 4.0); + ClearInput(); +} + +void Arc(SDL_Surface *s, int x, int y, int r, float dir) +{ + int bright; + int i, c; + float pdir, cdir, ndir; + + int l_x = x, l_y = y; + int cx, cy, c1x, c1y, c2x, c2y; + + bright = rand()%128+63; + i = 0; + while (i < r) { + i += rand()%5+25; + pdir = dir + (float)(rand()%16)/16.0*2.0*(M_PI / 15.0); + ndir = dir - (float)(rand()%16)/16.0*2.0*(M_PI / 15.0); + cdir = dir + (float)(rand()%16)/16.0*2.0*(M_PI / 20.0) - (float)(rand()%16)/16.0*2.0*(M_PI / 20.0); + + bright += rand()%30; + bright -= rand()%30; + + if (bright < 0) bright = 0; + if (bright > 255) bright = 255; + + c1x = x + cos(pdir) * i; + c1y = y + sin(pdir) * i; + ThinLine(s, l_x, l_y, c1x, c1y, bright); + c2x = x + cos(ndir) * i; + c2y = y + sin(ndir) * i; + ThinLine(s, l_x, l_y, c2x, c2y, bright); + + for (c = 0; c < 5; c++) { + DrawRect(x + cos(dir - (M_PI / 10.0) + (float)(rand()%16)/16.0*2.0*(M_PI / 10.0)) * i, y + sin(dir - (M_PI / 10.0) + + (float)(rand()%16)/16.0*2.0*(M_PI / 10.0)) * i, 1, 1, rand()%128+63); + } + + i += rand()%5+25; + cx = x + cos(cdir) * i; + cy = y + sin(cdir) * i; + ThinLine(s, c1x, c1y, cx, cy, bright); + ThinLine(s, c2x, c2y, cx, cy, bright); + l_x = cx; + l_y = cy; + } + +} + +int DungeonPlay(char *fname) +{ + int ix, iy; + int off_x, off_y; + int t = 0; + int i, j; + int lost_gems; + int rg_x, rg_y, rg_v; + int max_dist; + int last_killed = 0; + int n_arcs = 0; + int can_move; + + float arcdir; + + char buf[50]; + + expired_ms = 0; + LoadingScreen(0, 0.0); + if (fname[0] != 0) { + LoadGame(fname); + } + + RandomGenerateMap(); + InitEnemies(); + InitBossVars(); + + PlayerDefaultStats(); + if (game_load) { + first_game = 0; + ReadPlayerData(); + //Paint(rooms[0].x+1, rooms[0].y+1, rooms[0].w-2, rooms[0].h-2, "dat/d/fbossroom.loc"); + } else { + player_x = map.w * 32 / 2 - PLAYERW/2; + player_y = map.h * 32 / 2 - PLAYERH/2; + } + + InitAutomap(); + + if (game_load) CloseFile(); + + max_dist = 0; + for (i = 0; i < 3000; i++) { + if (rooms[i].s_dist > max_dist) { + max_dist = rooms[i].s_dist; + } + } + + game_running = 1; + while (game_running) { + //sprintf(buf, "X: %d Y: %d", (player_x + PLAYERW/2)/32*32 + PLAYERW/2, (player_y + PLAYERH/2)/32*32 + PLAYERH/2); + //SDL_WM_SetCaption(buf, "MT"); + if (!game_paused) { + if (player_dying > 30) { + player_hp--; + + if (player_hp <= 0) { + if (!training) player_lives--; + lost_gems = player_gems / 3; + player_gems -= lost_gems; + + lost_gems = lost_gems * 95 / 100; + while (lost_gems > 0) { + rg_x = rooms[player_room].x * 32 + 32 + rand()%(rooms[player_room].w*32-64); + rg_y = rooms[player_room].y * 32 + 32 + rand()%(rooms[player_room].h*32-64); + rg_v = rand() % (lost_gems / 4 + 2); + CreateGem(rg_x, rg_y, player_room, rg_v); + lost_gems -= rg_v; + } + + player_dying = 0; + shield_hp = 0; + + if ( (current_boss == 3) && (boss_fight_mode != 0) ) { + player_x = enter_room_x; + player_y = enter_room_y; + prv_player_room = 1; + } else { + player_x = checkpoint_x; + player_y = checkpoint_y; + } + scroll_home = 1; + CircuitBullets(player_x, player_y, 100); + player_hp = 3 + (player_shield == 30)*3; + } else { + player_dying = 0; + } + } + } + + circuit_size = 250 + 50*(circuit_fillrate + circuit_recoverrate); + + if (magic_circuit > 0) { + circuit_range = (sqrt(magic_circuit + 1) * 6 + min(magic_circuit / 2, 50))*1.66; + if (artifacts[3]) circuit_range += circuit_range / 2.4; + } else circuit_range = -1; + player_room = GetRoom(player_x/32, player_y/32); + + if (player_room != prv_player_room) { + SetTonedPalette((float)rooms[player_room].s_dist / (float)max_dist); + prv_player_room = player_room; + RecordRoom(player_room); + + enter_room_x = player_x; + enter_room_y = player_y; + + if (rooms[player_room].room_type == 2) { + // lock the doors + LockDoors(player_room); + // it's a boss room + BossRoom(player_room); + } + if (((rooms[player_room].checkpoint)||(player_room==0))&&(!artifacts[11])) { + checkpoint_x = rooms[player_room].x * 32 + (rooms[player_room].w / 2 * 32) + 8; + checkpoint_y = rooms[player_room].y * 32 + (rooms[player_room].h / 2 * 32) + 4; + } + if (rooms[player_room].visited == 0) { + rooms[player_room].visited = 1; + explored++; + + if (explored == 3000) { + agate_knife_loc = player_room; + } + + ActivateRoom(player_room); + } + } + + if (last_killed != killed_enemies) { + SetTonedPalette((float)rooms[player_room].s_dist / (float)max_dist); + last_killed = killed_enemies; + } else { + if ((player_room == 0)&&(artifacts[11] == 1)) { + SetTonedPalette(0); + } + } + + if (!map_enabled) { + ScrollTo(player_x + PLAYERW/2 - 320, player_y + PLAYERH/2 - 240); + DrawLevel(scroll_x, scroll_y, 1, 1); + //DrawLevel(player_x + 8 - 320, player_y + 12 - 240); + + if (player_dying == 0) { + DrawShield(); + + if (magic_circuit > 0) { + if (player_dying == 0) { + if (circuit_release == 0) { + arcdir = RandomDir(); + n_arcs = 1 + (circuit_size / 200 + 2) * magic_circuit / circuit_size; + for (i = 0; i < n_arcs; i++) { + Arc(screen, player_x - scroll_x + PLAYERW/2, player_y - scroll_y + PLAYERH/2, circuit_range, arcdir); + arcdir += (float)(rand()%16) / 16.0 * (M_PI*2/(float)n_arcs); + } + } + } + } + + DrawPlayer(312, 228, player_dir, player_wlk / wlk_wait); + } else { + if (t % 2 == 0) DrawPlayer(312, 228, player_dir, player_wlk / wlk_wait); + + if (!game_paused) + player_dying++; + } + t++; + if ((boss_fight_mode != 0)&&(boss_fight_mode < 23)&&(!game_paused)) { + BossControl(); + } + DrawEntities(); + if (!game_paused) MoveEntities(); + + if (boss_fight_mode == 2) { + DrawBossHP(100); + } + + if (rooms[player_room].room_type == 5) { + DrawPowerObject(); + } + if ( (rooms[player_room].room_type == 6) && (current_boss == 3) ) { + DrawPowerObject(); + } + if ((rooms[player_room].room_type == 4) && ((player_room % 1000) == 999)) { + DrawPowerObject(); + } + if (player_room == agate_knife_loc) { + { + static float agate_t = 0.0; + static SDL_Surface *agate_knife = NULL; + int xpos, ypos; + int room_w, room_h; + int room_x, room_y; + + room_x = rooms[player_room].x * 32 + 32; + room_y = rooms[player_room].y * 32 + 32; + room_w = rooms[player_room].w * 32 - 64; + room_h = rooms[player_room].h * 32 - 64; + + SDL_Rect draw_to; + if (agate_knife == NULL) { + agate_knife = IMG_Load("dat/i/agate.png"); + SDL_SetColorKey(agate_knife, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + } + xpos = (int)((sin(agate_t * 1.33)*0.5+0.5) * (float)room_w) + room_x; + ypos = (int)((cos(agate_t * 0.7)*0.5+0.5) * (float)room_h) + room_y; + + if (dist(player_x, player_y, xpos, ypos) < 20) { + agate_knife_loc = -1; + specialmessage = 50; + specialmessagetimer = 150; + SND_Pos("dat/a/crystal2.wav", 128, 0); + + player_shield = 30; + circuit_fillrate = 30; + circuit_recoverrate = 30; + player_hp = 6; + } + draw_to.x = xpos - 16 - scroll_x; + draw_to.y = ypos - 16 - scroll_y; + + SDL_BlitSurface(agate_knife, NULL, screen, &draw_to); + + agate_t += 0.05; + } + } + + if (opening_door_i > 0) { + DrawArtifactOverhead(opening_door_n); + for (i = 0; i < 5; i++) { + j = i * 50 - 250 + (opening_door_i * 5); + if (j > 0) { + DrawCircle(player_x - scroll_x, player_y - scroll_y, j, 255); + } + } + + if (!game_paused) { + opening_door_i++; + if (opening_door_i >= 100) { + opening_door_i = 0; + Put(opening_door_x, opening_door_y, Get(opening_door_x, opening_door_y) - 38 + 13, GetRoom(opening_door_x, opening_door_y)); + } + } + } + + if (circuit_release > 0) { + DrawCircle(release_x - player_x + 320, release_y - player_y + 240, circuit_release * release_range / 20, sin((float)circuit_release / 20.0)*127+127); + if (!game_paused) { + CircuitBullets(release_x, release_y, circuit_release * release_range / 20); + //HurtEnemies(release_x, release_y, circuit_release * release_range / 20, release_str); + circuit_release+=2; + + if (circuit_release > 24) { + circuit_release = 0; + HurtEnemies(release_x, release_y, release_range, release_str); + if (boss_fight_mode == 2) TryHurtBoss(release_x, release_y, release_range, release_str); + } + } + } + + if (!game_paused) { + if (shield_hp < player_shield) { + shield_recover += player_shield * 3 / (3 - training - (player_shield == 30)); + if (artifacts[1]) shield_recover += player_shield * 3 / (3 - training - (player_shield == 30)); + if (shield_recover >= 50) { + shield_hp++; + shield_recover -= 50 - (player_shield == 30)*25; + } + } + } + } + + DrawRect(0, 0, 640, 29, 0); + DrawRect(1, 1, 638, 27, 32); + DrawRect(2, 2, 636, 25, 64); + + if (!tele_select) { + sprintf(buf, "Psi Crystals: %d", player_gems); + draw_text(3, 3, buf, 200); + sprintf(buf, "Explored: %.1f%% (%d/%d rooms)", (float)explored/30.0, explored, 3000); + draw_text(3, 11, buf, 200); + sprintf(buf, "Cleared: %.1f%% (%d/%d monsters)", (float)killed_enemies/(float)total_enemies*100.0, killed_enemies, total_enemies); + draw_text(3, 19, buf, 200); + + draw_text(316, 3, "Reflect shield", (player_gems >= UpgradePrice(0))&&(player_shield!=30) ? (231 + (t%13)*2) : 200); + DrawMeter(434, 3, player_shield); + + draw_text(316, 11, "Circuit charge", (player_gems >= UpgradePrice(1))&&(circuit_fillrate!=30) ? (231 + (t%13)*2) : 200); + DrawMeter(434, 11, circuit_fillrate); + + draw_text(316, 19, "Circuit refill", (player_gems >= UpgradePrice(2))&&(circuit_recoverrate!=30) ? (231 + (t%13)*2) : 200); + DrawMeter(434, 19, circuit_recoverrate); + + } else { + draw_text(80, 11-6, "Use the movement keys to locate a checkpoint. Press ENTER to", 240); + draw_text(52, 11+6, "teleport to this checkpoint. Press ESCAPE or TAB once you are done.", 240); + } + + if (!training) { + buf[0] = 30; + + if (player_lives <= 99) { + if (player_lives < 10) { + sprintf(buf+1, " %d", player_lives); + } else { + sprintf(buf+1, "%d", player_lives); + } + } else { + sprintf(buf+1, "**"); + } + + draw_text(615, 4, buf, 200); + + DrawRect(615, 13, 24, 4, 240); + DrawRect(616, 14, 22, 2, 0); + i = (player_lives_part * 22 / 88); + if (i > 0) { + DrawRect(616, 14, i, 2, 160 + (t % 40)); + } + } + + if (player_shield != 30) { + for (i = 0; i < player_hp; i++) { + buf[i] = 3; + } + buf[player_hp]=0; + } else { + for (i = 0; i < (player_hp / 2); i++) { + buf[i] = 3; + } + if ((player_hp % 2) == 1) { + buf[(player_hp + 1) / 2 - 1] = 2; + } + buf[(player_hp+1)/2]=0; + } + + draw_text(615, 18 - (5*training), buf, 200); + + DrawRect(0, 466, 640, 14, 0); + DrawRect(1, 467, 638, 12, 32); + DrawRect(2, 468, 636, 10, 64); + + DrawCircuit(); + DrawArtifacts(); + + SpecialTile((player_x+PLAYERW/2)/32, (player_y+PLAYERH/2)/32); + + if (map_enabled) DisplayAutomap(); + + if ((boss_fight_mode != 0)&&(boss_fight_mode == 23)&&(!game_paused)) { + BossControl(); + } + if ( (boss_dlg != 0) && (!game_paused)) { + BossDialog(); + } + + if (game_paused && (!map_enabled) && (!voluntary_exit)) { + for (i = 0; i < 10; i++) { + DrawRect((640 - 6 * 8) / 2 - i, (480 - 8) / 2 - i, 6*8 + 2*i, 8 + 2*i, 64 - i*5); + } + draw_text((640 - 6 * 8) / 2, (480 - 8) / 2, "Paused", 255); + + { + int t_days; + int t_hours; + int t_minutes; + int t_seconds; + + t_seconds = (expired_ms / 1000) % 60; + t_minutes = ((expired_ms / 1000) / 60) % 60; + t_hours = (((expired_ms / 1000) / 60) / 60) % 24; + t_days = (((expired_ms / 1000) / 60) / 60) / 24; + + if (t_days > 0) { + sprintf(buf, "%dd %dh %dm %ds", t_days, t_hours, t_minutes, t_seconds); + } else { + if (t_hours > 0) { + sprintf(buf, "%dh %dm %ds", t_hours, t_minutes, t_seconds); + } else { + sprintf(buf, "%dm %ds", t_minutes, t_seconds); + } + } + draw_text(636 - strlen(buf)*8, 470, buf, 255); + } + } + + if (voluntary_exit) { + DrawRect(152, 200, 336, 80, 128); + DrawRect(160, 208, 320, 64, 64); + draw_text((640 - 30 * 8) / 2, (480 - 8) / 2 - 4, "Are you sure you want to quit?", 255); + draw_text((640 - 23 * 8) / 2, (480 - 8) / 2 + 4, "Press enter to confirm.", 255); + } + + VideoUpdate(); + + MusicUpdate(); + + EndCycle(0); + + can_move = 1; + + if ((player_dying != 0) && (player_hp <= 1)) can_move = 0; + if (rooms[player_room].room_type == 5) + if (CanGetArtifact()) + if (Get((player_x+PLAYERW/2)/32, (player_y+PLAYERH/2)/32)==42) + if (rooms[player_room].enemies == 0) + can_move = 0; + + if (rooms[player_room].room_type == 6) + if (CanGetArtifact()) + if (PlayerDist(rooms[player_room].w * 16 + rooms[player_room].x * 32, + rooms[player_room].h * 16 + rooms[player_room].y * 32) < 32) + if (rooms[player_room].enemies == 0) + if (current_boss == 3) + can_move = 0; + + if (scroll_home != 0) can_move = 0; + if (boss_fight_mode == 1) can_move = 0; + if (boss_fight_mode >= 3) can_move = 0; + if (opening_door_i != 0) can_move = 0; + if (game_paused) can_move = 0; + + HandleEvents(); + if (map_enabled) { + game_paused = 1; + } + + if (can_move) { + + ix = player_x; + iy = player_y; + off_x = 0; + off_y = 0; + if (key_held[K_UP] && !key_held[K_DN]) { + iy -= player_walk_speed * (artifacts[4]?1.4:1); + player_dir = 0; + } + if (key_held[K_DN] && !key_held[K_UP]) { + iy += player_walk_speed * (artifacts[4]?1.4:1);; + player_dir = 1; + off_y = 24; + } + if (key_held[K_LT] && !key_held[K_RT]) { + ix -= player_walk_speed * (artifacts[4]?1.4:1);; + if (!(key_held[K_UP] || key_held[K_DN])) { + player_dir = 3; + } + } + if (key_held[K_RT] && !key_held[K_LT]) { + off_x = 16; + ix += player_walk_speed * (artifacts[4]?1.4:1);; + if (!(key_held[K_UP] || key_held[K_DN])) { + player_dir = 2; + + } + } + if ((key_held[K_SP])&&(magic_circuit >= 0)) { + magic_circuit += (circuit_fillrate * (3+training+(circuit_fillrate==30))/3); + } else { + if (magic_circuit < 0) { + magic_circuit += (circuit_recoverrate * (3+training+(circuit_recoverrate==30))/3); + if (magic_circuit > 0) magic_circuit = 0; + } else { + if (magic_circuit > 0) { + ReleaseCircuit(); + } + } + } + + if (magic_circuit > circuit_size) magic_circuit = circuit_size; + + if ((ix!=player_x)||(iy!=player_y)) { + // Are we changing to a new square? + if (((player_x / 32)!=((ix+off_x) / 32)) || ((player_y / 32)!=((iy+off_y) / 32))) { + //printf("%d\n", tile); + if (TouchTile(ix, iy)) { + player_wlk = (player_wlk + 1 + artifacts[4]*3) % (4*wlk_wait); + } else { + if (TouchTile(player_x, iy)) { + player_wlk = (player_wlk + 1 + artifacts[4]*3) % (4*wlk_wait); + } else { + if (TouchTile(ix, player_y)) { + player_wlk = (player_wlk + 1 + artifacts[4]*3) % (4*wlk_wait); + if (off_x > 0) player_dir = 2; + else player_dir = 3; + } + } + + } + } else { + player_x = ix; + player_y = iy; + + player_wlk = (player_wlk + 1 + artifacts[4]*3) % (4*wlk_wait); + } + } + } + + if ((t % (33 * 10))==(33 * 10 - 1)) { + ActivateRand(); + } + + if (voluntary_exit && enter_pressed) { + voluntary_exit = 0; + game_running = 0; + game_paused = 0; + } + + if ((player_lives == 0) && (!training)) { + break; + } + if (show_ending) { + break; + } + } + + if (show_ending) { + show_ending = 0; + ShowEnding(); + } + + if ((player_lives == 0) && (!training)) { + SDL_FillRect(screen, NULL, 0); + draw_text(252, 236, "G A M E O V E R", 255); + VideoUpdate(); + SDL_Delay(2000); + } + + return 0; +} + +void UpRoom() +{ + int i, nd; + + nd = rooms[player_room].s_dist + 1; + + for (i = 0; i < 3000; i++) { + if (rooms[i].s_dist == nd) { + player_x = rooms[i].x * 32 + 64; + player_y = rooms[i].y * 32 + 64; + } + } +} + +void CancelVoluntaryExit() +{ + if (voluntary_exit) { + voluntary_exit = 0; + game_paused = 0; + } +} + +void HandleEvents() +{ + unsigned short db; + static SDL_Event event; + int pressed_tab = 0; + + if (PLAYBACK) { + db = fgetc(record_file); + db |= fgetc(record_file) << 8; + + key_held[K_UP] = (db & 0x0001)>0; + key_held[K_DN] = (db & 0x0002)>0; + key_held[K_LT] = (db & 0x0004)>0; + key_held[K_RT] = (db & 0x0008)>0; + key_held[K_SP] = (db & 0x0010)>0; + enter_pressed = (db & 0x0020)>0; + map_enabled = (db & 0x0040)>0; + game_running = (db & 0x0080)>0; + game_paused = (db & 0x0100)>0; + voluntary_exit = (db & 0x0200)>0; + pressed_tab = (db & 0x0400)>0; + tele_select = (db & 0x0800)>0; + + return; + } + + if (pressed_tab) { + c_scroll_x = player_x; + c_scroll_y = player_y; + } + + enter_pressed = 0; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_KEYDOWN) { + switch (event.key.keysym.sym) { + case SDLK_w: + case SDLK_UP: + key_held[K_UP] = 1; + CancelVoluntaryExit(); + break; + case SDLK_s: + case SDLK_DOWN: + key_held[K_DN] = 1; + CancelVoluntaryExit(); + break; + case SDLK_a: + case SDLK_LEFT: + key_held[K_LT] = 1; + CancelVoluntaryExit(); + break; + case SDLK_d: + case SDLK_RIGHT: + key_held[K_RT] = 1; + CancelVoluntaryExit(); + break; + case SDLK_SPACE: + key_held[K_SP] = 1; + CancelVoluntaryExit(); + break; + case SDLK_RETURN: + enter_pressed = 1; + break; + case SDLK_ESCAPE: + if (map_enabled) { + map_enabled = 0; + game_paused = 0; + tele_select = 0; + } else { + voluntary_exit ^= 1; + game_paused = voluntary_exit; + } + break; + case SDLK_TAB: + if (tele_select) { + map_enabled = 0; + game_paused = 0; + tele_select = 0; + } else { + pressed_tab = 1; + map_enabled ^= 1; + game_paused = map_enabled; + c_scroll_x = player_x; + c_scroll_y = player_y; + } + CancelVoluntaryExit(); + break; + case SDLK_h: + CancelVoluntaryExit(); + ShowHelp(); + break; + case SDLK_p: + game_paused ^= 1; + CancelVoluntaryExit(); + break; + + + /* + case SDLK_j: + { + player_shield = 20; + circuit_recoverrate = 20; + circuit_fillrate = 20; + } + break; + case SDLK_k: + { + int i, n, j; + for (j = 0; j < 1; j++) { + for (i = 0; i < 50000; i++) { + n = rand()%3000; + if (rooms[n].visited == 0) { + player_x = rooms[n].x * 32 + rooms[n].w * 16; + player_y = rooms[n].y * 32 + rooms[n].h * 16; + rooms[n].visited = 1; + explored++; + break; + } + } + } + } + break; + + case SDLK_m: + { + int i; + for (i = 0; i < 8; i++) { + artifacts[i] = 1; + } + for (i = 8; i < 11; i++) { + artifacts[i] = 0; + } + artifacts[11] = 0; + } + break; + + case SDLK_n: + { + current_boss = 3; + expired_ms = 1000000; + } + break; + */ + default: + break; + } + } + if (event.type == SDL_KEYUP) { + switch (event.key.keysym.sym) { + case SDLK_w: + case SDLK_UP: + key_held[K_UP] = 0; + break; + case SDLK_s: + case SDLK_DOWN: + key_held[K_DN] = 0; + break; + case SDLK_a: + case SDLK_LEFT: + key_held[K_LT] = 0; + break; + case SDLK_d: + case SDLK_RIGHT: + key_held[K_RT] = 0; + break; + case SDLK_SPACE: + key_held[K_SP] = 0; + break; + default: + break; + } + } + if (event.type == SDL_QUIT) { + voluntary_exit = 1; + } + } + + if (RECORDING) { + db = 0; + + db |= 0x0001 * key_held[K_UP]; + db |= 0x0002 * key_held[K_DN]; + db |= 0x0004 * key_held[K_LT]; + db |= 0x0008 * key_held[K_RT]; + db |= 0x0010 * key_held[K_SP]; + db |= 0x0020 * enter_pressed; + db |= 0x0040 * map_enabled; + db |= 0x0080 * game_running; + db |= 0x0100 * game_paused; + db |= 0x0200 * voluntary_exit; + db |= 0x0400 * pressed_tab; + db |= 0x0800 * tele_select; + + fputc(db & 0x00FF, record_file); + fputc((db & 0xFF00)>>8, record_file); + return; + } + +} + +void DrawLevel(int off_x, int off_y, int hide_not_visited, int fog_of_war) +{ + static SDL_Surface *tiles = NULL; + static SDL_Surface *fog = NULL; + Uint8 *pp; + SDL_Rect tilerec, screenrec; + int x, y, i; + int resolve_x, resolve_y; + + DrawRect(0, 0, 640, 480, 255); + + if (tiles == NULL) { + tiles = IMG_Load("dat/i/tileset.png"); + fog = IMG_Load("dat/i/tileset.png"); + + pp = fog->pixels; + + for (i = 0; i < fog->w*fog->h; i++) { + *pp = *pp / 2 + 128; + pp++; + } + } + for (y = 0; y < 16; y++) { + for (x = 0; x < 21; x++) { + resolve_x = x + (off_x/32); + resolve_y = y + (off_y/32); + + if ((GetVisited(resolve_x, resolve_y) == 0)&&(player_room != GetRoom(resolve_x, resolve_y))&&(hide_not_visited)) { + tilerec.x = 17 * 32; + } else { + tilerec.x = Get(resolve_x, resolve_y) * 32; + } + tilerec.y = 0; + tilerec.w = 32; + tilerec.h = 32; + + screenrec.x = x*32 - ( (off_x) %32); + screenrec.y = y*32 - ( (off_y) %32); + + if ((player_room != GetRoom(resolve_x, resolve_y))&&(fog_of_war)) { + SDL_BlitSurface(fog, &tilerec, screen, &screenrec); + } else { + SDL_BlitSurface(tiles, &tilerec, screen, &screenrec); + } + } + } +} + +void DrawPlayer(int x, int y, int pl_dir, int pl_frm) +{ + static SDL_Surface *playersprite = NULL; + SDL_Rect playerrec, screenrec; + + if (playersprite == NULL) { + playersprite = IMG_Load("dat/i/player.png"); + SDL_SetColorKey(playersprite, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + } + + playerrec.x = pl_frm * 16; + playerrec.y = pl_dir * 24; + playerrec.w = 16; + playerrec.h = 24; + + screenrec.x = x; + screenrec.y = y; + + SDL_BlitSurface(playersprite, &playerrec, screen, &screenrec); +} + +void SetGreyscalePalette() +{ + SDL_Color grey[256]; + SDL_Color pal[256]; + int i; + + float ip; + + for (i = 0; i < 256; i++) { + grey[i].r = grey[i].g = grey[i].b = i; + } + + for (i = 0; i < 256; i++) { + ip = (float)i / 255.0; + pal[i].r = (cos(ip * M_PI / 2.0 + M_PI) + 1.0) * 255; + pal[i].g = (sin(ip * M_PI / 2.0) * 255 + i) / 2; + pal[i].b = sin(ip * M_PI / 2.0) * 255; + } + + SDL_SetPalette(screen, SDL_LOGPAL, grey, 0, 256); + SDL_SetPalette(screen, SDL_PHYSPAL, pal, 0, 256); +} + +void SetTonedPalette(float dct) +{ + SDL_Color pal[256]; + float pct = 1.0 - dct; + float rp_dct, rp_pct; + float ip; + int ec; + int i; + static int tk = 0; + + ec = rooms[player_room].enemies; + + if (ec < 50) { + rp_dct = (float)ec / 50.0; + } else { + rp_dct = 1.0; + } + rp_pct = 1.0 - rp_dct; + + if ( (player_room == 0) && (current_boss == 3) && (boss_fight_mode >= 3) ) { + if (boss_fight_mode == 23) { + for (i = 0; i < 256; i++) { + pal[i].r = i; + pal[i].g = i; + pal[i].b = i; + } + } else { + tk++; + pct = sin((float)tk / 20.0 * M_PI) * (0.5 - (float)(boss_fight_mode-3)*0.025) + (0.5 - (float)(boss_fight_mode-3)*0.025); + + if (magic_circuit < 0.1) pct = 1.0; + + for (i = 0; i < 256; i++) { + ip = (float)i / 255.0; + pal[i].r = 255 - (255 - (cos(ip * M_PI / 2.0 + M_PI) + 1.0) * 255)*pct; + pal[i].g = 255 - (255 - i)*pct; + pal[i].b = 255 - (255 - sin(ip * M_PI / 2.0) * 255) * pct; + } + + + pal[1].r = 0; + pal[1].g = 0; + pal[1].b = 0; + } + } else { + if (artifacts[11]) { + if (player_room == 0) { + tk++; + pct = sin((float)tk / 33.0 * M_PI) * 0.5 + 0.5; + for (i = 0; i < 256; i++) { + pal[i].r = i; + pal[i].g = (i / 3)*pct; + pal[i].b = (i * 2 / 3)*pct; + } + } else { + for (i = 0; i < 256; i++) { + ip = (float)i / 255.0; + pal[i].r = i; + pal[i].g = i * dct; + pal[i].b = (cos(ip * M_PI / 2.0 + M_PI) + 1.0) * 255 * dct; + } + } + + if ( (current_boss == 3) && (player_shield == 30) && (player_room == 0)) { + if (boss_lives <= 1) { + tk++; + for (i = 0; i < 256; i++) { + pct = sin((float) (tk + i) / 24.0 * M_PI) * 0.5 + 0.5; + + pal[i].r = (i * 0.5 + 128)*pct; + pal[i].g = i * 0.5 + 128; + pal[i].b = (i * 0.5 + 128)*pct; + } + } + } + } else { + for (i = 0; i < 256; i++) { + ip = (float)i / 255.0; + pal[i].r = (((cos(ip * M_PI / 2.0 + M_PI) + 1.0) * 255)*pct + i*dct)*rp_pct + (sin(ip * M_PI / 2.0) * 207 + 48)*rp_dct; + pal[i].g = (i)*rp_pct + ((cos(ip * M_PI / 2.0 + M_PI) + 1.0) * 255)*rp_dct; + pal[i].b = ((sin(ip * M_PI / 2.0) * 255 * pct)+((cos(ip * M_PI / 2.0 + M_PI) + 1.0) * 255 * dct))*rp_pct + ((cos(ip * M_PI / 2.0 + M_PI) + 1.0) * 255)*rp_dct; + } + } + } + + SDL_SetPalette(screen, SDL_PHYSPAL, pal, 0, 256); +} + +void SetTitlePalette(int curve_start, int curve_end) +{ + SDL_Color pal[256]; + int ec; + int i; + + for (i = 0; i < 256; i++) { + ec = (i - curve_start) * 255 / (curve_end-curve_start); + if (ec < 0) ec = 0; + if (ec > 255) ec = 255; + + pal[i].r = ec; + pal[i].g = ec; + pal[i].b = ec; + } + + SDL_SetPalette(screen, SDL_PHYSPAL, pal, 0, 256); +} + +void SetTitlePalette2(int t) +{ + SDL_Color pal[256]; + int i; + + float ip; + float bright; + float b_coeff; + + bright = 1 - ((float)t / 30.0); + if (bright < 0.0) bright = 0.0; + b_coeff = 1 - bright; + + for (i = 0; i < 256; i++) { + ip = (float)i / 255.0; + pal[i].r = (cos(ip * M_PI / 2.0 + M_PI) + 1.0) * 255 * b_coeff + 255*bright; + pal[i].g = (sin(ip * M_PI / 2.0) * 255 + i) / 2 * b_coeff + 255*bright; + pal[i].b = sin(ip * M_PI / 2.0) * 255 * b_coeff + 255*bright; + } + + SDL_SetPalette(screen, SDL_PHYSPAL, pal, 0, 256); +} + +int IsSolid(unsigned char tile) +{ + return TileData[tile].Is_Solid; +} + +void ActivateBossDoor(int x, int y) +{ + static int bd_timer = 0; + int bx = x, by = y; + + // find boss room + if (rooms[GetRoom(x+1, y)].room_type == 2) { + bx += 1; + } else + if (rooms[GetRoom(x-1, y)].room_type == 2) { + bx -= 1; + } else + if (rooms[GetRoom(x, y+1)].room_type == 2) { + by += 1; + } else + if (rooms[GetRoom(x, y-1)].room_type == 2) { + by -= 1; + } else + return; + + if (artifacts[8 + rooms[GetRoom(bx, by)].room_param]) { + opening_door_x = x; + opening_door_y = y; + opening_door_i = 1; + opening_door_n = rooms[GetRoom(bx, by)].room_param; + if ((SDL_GetTicks() - bd_timer) > 100) { + SND_Pos("dat/a/crystal2.wav", 100, 0); + bd_timer = SDL_GetTicks(); + } + } +} + +int TouchTile(int ix, int iy) +{ + int i; + int off_x, off_y; + int ret = 1; + unsigned char tile; + + for (i = 0; i < 4; i++) { + off_x = 15*(i%2); + off_y = 23*(i/2); + + tile = Get((ix+off_x)/32, (iy+off_y)/32); + switch (tile) { + case 38: + case 39: + case 40: + case 41: + ActivateBossDoor((ix+off_x)/32, (iy+off_y)/32); + ret = 0; + break; + case 13: + player_x = (ix + off_x) / 32 * 32 + 8; + player_y = (iy/32 + 2)*32 + 32; + return 1; + break; + case 14: + player_x = (ix + off_x) / 32 * 32 + 8; + player_y = (iy/32 - 2)*32 + 8; + return 1; + break; + case 15: + player_x = (ix/32 + 2)*32 + 32; + player_y = (iy + off_y) / 32 * 32 + 4; + return 1; + break; + case 16: + player_x = (ix/32 - 2)*32 + 16; + player_y = (iy + off_y) / 32 * 32 + 4; + return 1; + break; + default: + if (TileData[tile].Is_Solid) ret = 0; + //ret = 0; + break; + } + } + if (ret == 1) { + player_x = ix; + player_y = iy; + } + return ret; +} + +void text_init() +{ + FILE *font_data_file; + int chr, x, y; + font_data_file = fopen("dat/d/font.dat", "rb"); + + for (chr = 0; chr < 128; chr++) { + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + font_data[chr][x][y] = fgetc(font_data_file); + } + } + } + + fclose(font_data_file); +} + +void draw_char(int cur_x, int cur_y, int c, Uint8 tcol) +{ + int px, py; + Uint8 *pix; + + for (py = 0; py < 8; py++) { + pix = (Uint8 *)screen->pixels; + pix += (py+cur_y)*screen->w; + pix += cur_x; + + if ((cur_x >= 0)&&(py+cur_y >= 0)&&(cur_x < screen->w-8)&&(py+cur_y < screen->h)) { + for (px = 0; px < 8; px++) { + if (font_data[c][px][py] == 255) { + *pix = tcol; + } + if ((font_data[c][px][py] < 255)&&(font_data[c][px][py] > 0)) { + *pix = ((int)tcol * font_data[c][px][py] / 256) + ((int)*pix * (256-font_data[c][px][py]) / 256); + } + pix++; + } + } + } +} + +void draw_text(int x, int y, char *str, Uint8 tcol) +{ + int c, cur_x, cur_y; + + cur_x = x; + cur_y = y; + + while (*str != 0) { + c = *(str++); + if (c == '\n') { + cur_x = x; + cur_y+=10; + } else { + draw_char(cur_x, cur_y, c, tcol); + cur_x+=8; + } + } +} + +void draw_text_ex(int x, int y, char *str, Uint8 tcol, SDL_Surface *srf) +{ + Uint8 *pix; + int c, cur_x, cur_y, px, py; + + cur_x = x; + cur_y = y; + + while (*str != 0) { + c = *(str++); + if (c == '\n') { + cur_x = x; + cur_y+=8; + } else { + for (py = 0; py < 8; py++) { + pix = (Uint8 *)srf->pixels; + pix += (py+cur_y)*srf->w; + pix += cur_x; + for (px = 0; px < 8; px++) { + if (font_data[c][px][py]) { + *pix = tcol; + } + pix++; + } + } + cur_x+=8; + } + } +} + +void LockDoors(int r) +{ + //printf("Locking room %d...", r); + int x, y; + int rx, ry; + int rt; + int rcount = 0; + + for (y = 0; y < rooms[r].h; y++) { + for (x = 0; x < rooms[r].w; x++) { + rx = x + rooms[r].x; + ry = y + rooms[r].y; + rt = Get(rx, ry); + + if ((rt >= 13) && (rt <= 16)) { + rcount++; + Put(rx, ry, rt - 13 + 21, r); + } + } + } + //printf("locked %d doors\n", rcount); +} + +void ActivateRoom(int room) +{ + //printf("Activating room %d (type %d)\n", room, rooms[room].room_type); + if (rooms[room].checkpoint) { + checkpoints_found++; + } + if (rooms[room].room_type == 3) { + // lock the doors! + LockDoors(room); + } + ActivateEnemies(room); +} + +void DrawRect(int x, int y, int w, int h, unsigned char c) +{ + SDL_Rect r; + + r.x = x; + r.y = y; + r.w = w; + r.h = h; + + SDL_FillRect(screen, &r, c); +} + +void DrawCircuit() +{ + int vd = 520; + char buf[20]; + + if (magic_circuit != 0) { + DrawRect(110, 469, 8+abs(magic_circuit) * vd / circuit_size, 9, (magic_circuit > 0) ? 159 : 72); + DrawRect(111, 470, 6+abs(magic_circuit) * vd / circuit_size, 7, (magic_circuit > 0) ? 183 : 80); + DrawRect(112, 471, 4+abs(magic_circuit) * vd / circuit_size, 5, (magic_circuit > 0) ? 207 : 96); + DrawRect(113, 472, 2+abs(magic_circuit) * vd / circuit_size, 3, (magic_circuit > 0) ? 231 : 112); + DrawRect(114, 473, abs(magic_circuit) * vd / circuit_size, 1, (magic_circuit > 0) ? 255 : 128); + } + sprintf(buf, "%.1f", fabs((float)magic_circuit / 100.0)); + draw_text(115, 470, buf, 0); + draw_text(3, 469, "Psi Circuit", 200); +} + +void ReleaseCircuit() +{ + circuit_release = 1; + release_range = circuit_range; + release_x = player_x; + release_y = player_y; + release_str = magic_circuit; + if (circuit_fillrate==30) { + release_str *= 1.25; + } + + SND_CircuitRelease(release_str); + magic_circuit *= -1; +} + +void DrawCircle(int x, int y, int r, unsigned char c) +{ + int circ_y; + + int len_x, outer_len_x, inner_len_x; + + int inner_r = r - 10; + if (inner_r < 1) inner_r = 1; + + if (r < 1) return; + // a^2 + b^2 = c^2 + for (circ_y = 0; circ_y < r; circ_y++) { + if (circ_y < (r-10)) { + outer_len_x = sqrt(r*r - circ_y*circ_y); + inner_len_x = sqrt((r-10)*(r-10) - circ_y*circ_y); + DrawRect(x - outer_len_x, y - circ_y, (outer_len_x - inner_len_x), 1, c); + DrawRect(x + inner_len_x, y - circ_y, (outer_len_x - inner_len_x), 1, c); + DrawRect(x - outer_len_x, y + circ_y, (outer_len_x - inner_len_x), 1, c); + DrawRect(x + inner_len_x, y + circ_y, (outer_len_x - inner_len_x), 1, c); + } else { + len_x = sqrt(r*r - circ_y*circ_y); + + DrawRect(x - len_x, y - circ_y, len_x*2, 1, c); + DrawRect(x - len_x, y + circ_y, len_x*2, 1, c); + } + } +} + +void DrawCircleEx(int x, int y, int r, int r2, unsigned char c) +{ + int circ_y; + + int len_x, outer_len_x, inner_len_x; + + int inner_r = r2; + int diffi = r-r2; + if (inner_r < 1) inner_r = 1; + + + + if (r < 1) return; + // a^2 + b^2 = c^2 + for (circ_y = 0; circ_y < r; circ_y++) { + if (circ_y < (r-diffi)) { + outer_len_x = sqrt(r*r - circ_y*circ_y); + inner_len_x = sqrt((r-diffi)*(r-diffi) - circ_y*circ_y); + DrawRect(x - outer_len_x, y - circ_y, (outer_len_x - inner_len_x), 1, c); + DrawRect(x + inner_len_x, y - circ_y, (outer_len_x - inner_len_x), 1, c); + DrawRect(x - outer_len_x, y + circ_y, (outer_len_x - inner_len_x), 1, c); + DrawRect(x + inner_len_x, y + circ_y, (outer_len_x - inner_len_x), 1, c); + } else { + len_x = sqrt(r*r - circ_y*circ_y); + + DrawRect(x - len_x, y - circ_y, len_x*2, 1, c); + DrawRect(x - len_x, y + circ_y, len_x*2, 1, c); + } + } +} + +void DrawShield() +{ + static int t=0; + int s_size; + int belts = 0; + int i, bpos; + t++; + + if (player_shield == 0) return; + if (shield_hp == 0) return; + + s_size = shield_hp; + if (s_size > 15) { + belts = s_size - 15; + s_size = 15; + } + DrawCircleEx(320, 240, 28+s_size, 28-s_size, 128 + (shield_hp*127/player_shield) - (50*(shield_hp= 24) return; + if (player_gems >= UpgradePrice(0)) { + player_gems -= UpgradePrice(0); + player_shield += 1; + SND_Pos("dat/a/crystal.wav", 128, 0); + } + break; + case 29: + if (circuit_fillrate >= 24) return; + if (player_gems >= UpgradePrice(1)) { + player_gems -= UpgradePrice(1); + circuit_fillrate += 1; + SND_Pos("dat/a/crystal.wav", 128, 0); + } + break; + case 30: + if (circuit_recoverrate >= 24) return; + if (player_gems >= UpgradePrice(2)) { + player_gems -= UpgradePrice(2); + circuit_recoverrate += 1; + SND_Pos("dat/a/crystal.wav", 128, 0); + } + break; + case 31: + DoSaveGame(); + break; + case 32: + CrystalSummon(); + SND_Pos("dat/a/crystal.wav", 80, 0); + break; + default: + break; + } +} + +void CompassPoint() +{ + int nearest = 1000000; + int n_room = -1; + int i; + int loc_x, loc_y; + int cdist; + int rplx, rply; + int bosses_defeated = current_boss; + float pdir_1 = 0; + float pdir_2 = 0; + int pdir_1t = 0, pdir_2t = 0; + + rplx = player_x + PLAYERW/2; + rply = player_y + PLAYERH/2; + // Find the nearest SIGNIFICANT LOCATION for the player + + // Look at the three artifacts + // Unless the player is going for the place of power + + if (current_boss < 3) { + for (i = 0; i < 3; i++) { + // Has the player got this artifact already? + if (artifacts[8+i] == 0) { // no + // Has the player already destroyed the boss? + if (rooms[i * 1000 + 999].room_type == 2) { // no + // Can the player get the artifact? + if (CanGetArtifact()) { + // Point player to this artifact room, if it is the nearest + loc_x = rooms[i * 1000 + 499].x * 32 + rooms[i * 1000 + 499].w * 16; + loc_y = rooms[i * 1000 + 499].y * 32 + rooms[i * 1000 + 499].h * 16; + cdist = dist(rplx, rply, loc_x, loc_y); + if (cdist < nearest) { + nearest = cdist; + n_room = i * 1000 + 499; + } + } + } + } else { // has artifact + // Has the player already destroyed the boss? + if (rooms[i * 1000 + 999].room_type == 2) { // no + // Point player to the boss room, if it is the nearest + loc_x = rooms[i * 1000 + 999].x * 32 + rooms[i * 1000 + 999].w * 16; + loc_y = rooms[i * 1000 + 999].y * 32 + rooms[i * 1000 + 999].h * 16; + cdist = dist(rplx, rply, loc_x, loc_y); + if (cdist < nearest) { + nearest = cdist; + n_room = i * 1000 + 999; + } + } else { // yes + bosses_defeated++; + } + } + } + } + // If, on the other hand, the player has destroyed all three bosses, point them towards the + // PLACE OF POWER + if (bosses_defeated == 3) { + // If the player already has the seal, point them to home + if (artifacts[11] == 1) { + loc_x = rooms[0].x * 32 + rooms[0].w * 16; + loc_y = rooms[0].y * 32 + rooms[0].h * 16; + cdist = dist(rplx, rply, loc_x, loc_y); + if (cdist < nearest) { + nearest = cdist; + n_room = 0; + } + } else { + // Can the player touch the seal? + if (CanGetArtifact()) { + loc_x = rooms[place_of_power].x * 32 + rooms[place_of_power].w * 16; + loc_y = rooms[place_of_power].y * 32 + rooms[place_of_power].h * 16; + cdist = dist(rplx, rply, loc_x, loc_y); + if (cdist < nearest) { + nearest = cdist; + n_room = place_of_power; + } + } + } + } + + // Did we find a room? If so, point to it + + if (n_room != -1) { + loc_x = rooms[n_room].x * 32 + rooms[n_room].w * 16; + loc_y = rooms[n_room].y * 32 + rooms[n_room].h * 16; + + pdir_1 = PlayerDir(loc_x, loc_y) + M_PI; + pdir_1t = 1; + + n_room = -1; + } + + nearest = 1000000; + // Find the nearest uncleared artifact room + for (i = 0; i < 3000; i++) { + if (rooms[i].room_type == 3) { + loc_x = rooms[i].x * 32 + rooms[i].w * 16; + loc_y = rooms[i].y * 32 + rooms[i].h * 16; + cdist = dist(rplx, rply, loc_x, loc_y); + if (cdist < nearest) { + nearest = cdist; + n_room = i; + } + } + } + + if (n_room != -1) { + loc_x = rooms[n_room].x * 32 + rooms[n_room].w * 16; + loc_y = rooms[n_room].y * 32 + rooms[n_room].h * 16; + + pdir_2 = PlayerDir(loc_x, loc_y) + M_PI; + pdir_2t = 1; + + n_room = -1; + } + + // Did we find at least one thing to point to? If not, abort + if (!(pdir_1t || pdir_2t)) + return; + + DrawCircleEx(rplx - scroll_x, rply - scroll_y, 200, 190, 255); + if (pdir_1t) + DrawCircleEx(rplx - scroll_x + cos(pdir_1) * 170, rply - scroll_y + sin(pdir_1) * 170, 30, 20, 255); + if (pdir_2t) + DrawCircleEx(rplx - scroll_x + cos(pdir_2) * 170, rply - scroll_y + sin(pdir_2) * 170, 30, 20, 195); + + for (i = 0; i < 50; i++) { + if (pdir_1t) + DrawCircle(rplx - scroll_x + cos(pdir_1) * (25 + i * 4), rply - scroll_y + sin(pdir_1) * (25 + i * 4), 5, 255); + if (pdir_2t) + DrawCircle(rplx - scroll_x + cos(pdir_2) * (25 + i * 4), rply - scroll_y + sin(pdir_2) * (25 + i * 4), 5, 195); + } + DrawCircleEx(rplx - scroll_x, rply - scroll_y, 30, 20, 255); + + DrawCircleEx(rplx - scroll_x, rply - scroll_y, 197, 193, 128); + if (pdir_1t) + DrawCircleEx(rplx - scroll_x + cos(pdir_1) * 170, rply - scroll_y + sin(pdir_1) * 170, 27, 23, 128); + if (pdir_2t) + DrawCircleEx(rplx - scroll_x + cos(pdir_2) * 170, rply - scroll_y + sin(pdir_2) * 170, 27, 23, 78); + + for (i = 0; i < 50; i++) { + if (pdir_1t) + DrawCircle(rplx - scroll_x + cos(pdir_1) * (25 + i * 4), rply - scroll_y + sin(pdir_1) * (25 + i * 4), 3, 128); + if (pdir_2t) + DrawCircle(rplx - scroll_x + cos(pdir_2) * (25 + i * 4), rply - scroll_y + sin(pdir_2) * (25 + i * 4), 3, 78); + } + DrawCircleEx(rplx - scroll_x, rply - scroll_y, 27, 23, 128); +} + +void SpecialTile(int x, int y) +{ + static int otext = 0; + static int t = 0; + unsigned char tile; + char message[100] = ""; + + tile = Get(x, y); + switch (tile) { + case 25: + if (artifacts[11]) { + sprintf(message, "This is a checkpoint, but it doesn't seem to be working"); + break; + } + if (checkpoints_found <= 1) { + sprintf(message, "This is a checkpoint. You will return here when you die."); + } else { + sprintf(message, "Press ENTER to teleport between checkpoints."); + } + break; + case 26: + sprintf(message, "Press ENTER to open the storage chest"); + break; + case 28: + if (player_shield >= 25) { + sprintf(message, "Your shield is already at full efficiency"); + } else { + sprintf(message, "Press ENTER to upgrade shields (%d crystals)", UpgradePrice(0)); + } + break; + case 29: + if (circuit_fillrate >= 25) { + sprintf(message, "Your circuit charge rate is already at its highest"); + } else { + sprintf(message, "Press ENTER to upgrade circuit charge (%d crystals)", UpgradePrice(1)); + } + break; + case 30: + if (circuit_recoverrate >= 25) { + sprintf(message, "Your circuit refill rate is already at its highest"); + } else { + sprintf(message, "Press ENTER to upgrade circuit refill (%d crystals)", UpgradePrice(2)); + } + break; + case 31: + sprintf(message, "Press ENTER to record your progress"); + break; + case 32: + if (total_gems == 0) { + sprintf(message, "This is a crystal device. It isn't working at the moment."); + } else { + sprintf(message, "Press ENTER to activate the crystal device"); + } + break; + case 42: + if (rooms[player_room].room_type == 5) { + if (CanGetArtifact(rooms[player_room].room_param)) { + + } else { + sprintf(message, "The artifact is tainted with shadow. You must slay more of the shadow first."); + } + } + break; + case 53: + CompassPoint(); + break; + default: + if (first_game) { + if (otext < 60) { + sprintf(message, "Press H to read the help file"); + otext++; + } + } + break; + } + + if (message[0] == 0) { + if (specialmessage != 0) { + switch (specialmessage) { + case 1: sprintf(message, "Ancient artifact: Complete Map"); break; + case 2: sprintf(message, "Ancient artifact: Shield boost"); break; + case 3: sprintf(message, "Ancient artifact: Extra crystal efficiency"); break; + case 4: sprintf(message, "Ancient artifact: Circuit booster"); break; + case 5: sprintf(message, "Ancient artifact: Metabolism increase"); break; + case 6: sprintf(message, "Ancient artifact: Dodge enhancer"); break; + case 7: sprintf(message, "Ancient artifact: Ethereal Monocle"); break; + case 8: sprintf(message, "Ancient artifact: Crystal gatherer"); break; + + case 10: sprintf(message, "Enhancement: Shield upgrade"); break; + case 11: sprintf(message, "Enhancement: Circuit charge upgrade"); break; + case 12: sprintf(message, "Enhancement: Circuit refill upgrade"); break; + + case 20: sprintf(message, "Reward: Psi crystals"); break; + + case 30: sprintf(message, "Holy Sword 'Balmung' answers your call"); break; + case 31: sprintf(message, "Mystic Halberd 'Amenonuhoko' answers your call"); break; + case 32: sprintf(message, "Divine Bow 'Gandiva' answers your call"); break; + case 33: sprintf(message, "You capture the cursed seal. Return to the entrance"); break; + + case 40: sprintf(message, "Balmung will remain here, where the ley lines are strong"); break; + case 41: sprintf(message, "Amenonuhoko will remain here, where the ley lines are strong"); break; + case 42: sprintf(message, "Gandiva will remain here, where the ley lines are strong"); break; + + case 50: sprintf(message, ". . . . . . retrieved 'Agate Knife'"); break; + + default: sprintf(message, "ERROR: NO MESSAGE VALUE GIVEN"); break; + } + specialmessagetimer--; + if (specialmessagetimer <= 0) { + specialmessage = 0; + } + } + } + + if (message[0] == 0) return; + + DrawRect(320 - strlen(message)*8 / 2 - 20, 100, strlen(message)*8+40, 48, 200); + DrawRect(320 - strlen(message)*8 / 2 - 15, 105, strlen(message)*8+30, 38, 32); + DrawRect(320 - strlen(message)*8 / 2 - 10, 110, strlen(message)*8+20, 28, 64); + + draw_text(320 - strlen(message)*8 / 2, 120, message, t%16<8 ? 255 : 192); + t++; + if (enter_pressed) { + ActivateTile(tile, x, y); + } +} + +void ScrollTo(int x, int y) +{ + static int scrollspeed_x = 1, scrollspeed_y = 1; + if (scroll_home == 0) { + scroll_x = x; + scroll_y = y; + return; + } + + if (scroll_home == 1) { + scrollspeed_x = (x - scroll_x)/20; + scrollspeed_y = (y - scroll_y)/20; + scroll_home = 2; + } + + if (scroll_home == 2) { + scroll_x += (x - scroll_x)/2; + scroll_y += (y - scroll_y)/2; + + if ((abs(scroll_x-x)<2)&&(abs(scroll_y-y)<2)) { + scroll_x = x; + scroll_y = y; + scroll_home = 0; + } + } +} + +void DrawArtifacts() +{ + int i; + SDL_Rect from, to; + + if (artifact_spr == NULL) { + artifact_spr = IMG_Load("dat/i/artifacts.png"); + SDL_SetColorKey(artifact_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0); + } + + for (i = 0; i < 12; i++) { + if (artifacts[i]) { + from.x = i * 32; + from.y = 0; + from.w = 32; + from.h = 32; + + to.x = 608; + to.y = 47 + i * 35; + SDL_BlitSurface(artifact_spr, &from, screen, &to); + } + } +} + +void Swap(int *a, int *b) +{ + *a ^= *b ^= *a ^= *b; +} + +void ThinLine(SDL_Surface *scr, int x1, int y1, int x2, int y2, Uint8 col) +{ + int dx, dy, dm; + int i, j; + + dx = (x2 - x1); + dy = (y2 - y1); + + dm = abs(dx) > abs(dy) ? dx : dy; + + if (dm == 0) return; + + if (dm < 0) { + Swap(&x1, &x2); + Swap(&y1, &y2); + dx = (x2 - x1); + dy = (y2 - y1); + + dm = dm * -1; + } + + if (dm == dx) { + if (dy == 0) { + DrawRect(x1, y1, x2-x1+1, 1, col); + return; + } + for (i = 0; i < dm; i++) { + j = (dy * i / dm); + DrawRect(i+x1, j+y1, 1, 1, col); + } + } + if (dm == dy) { + if (dx == 0) { + DrawRect(x1, y1, 1, y2-y1+1, col); + return; + } + for (i = 0; i < dm; i++) { + j = (dx * i / dm); + DrawRect(j+x1, i+y1, 1, 1, col); + } + } +} diff --git a/src/levelblit.h b/src/levelblit.h new file mode 100755 index 0000000..ede321f --- /dev/null +++ b/src/levelblit.h @@ -0,0 +1,105 @@ +// +// levelblit.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// Exposes levelblit.c functionality and types + +#ifndef LEVELBLIT_H +#define LEVELBLIT_H + +#define PLAYERW 16 +#define PLAYERH 24 + +extern SDL_Surface *screen; + +extern int player_x, player_y; +extern int scroll_x, scroll_y; +extern int player_room; +extern int prv_player_room; + +extern int magic_circuit; +extern int circuit_size; +extern int circuit_range; +void DrawCircle(int x, int y, int r, unsigned char c); +void DrawCircleEx(int x, int y, int r, int r2, unsigned char c); +void DrawRect(int x, int y, int w, int h, unsigned char c); +int IsSolid(unsigned char tile); +void draw_char(int cur_x, int cur_y, int c, Uint8 tcol); +void draw_text(int x, int y, char *str, Uint8 tcol); +void draw_text_ex(int x, int y, char *str, Uint8 tcol, SDL_Surface *srf); + +extern int player_shield; +extern int shield_hp; +extern int shield_recover; +extern int player_hp; +extern int player_lives; +extern int player_lives_part; +extern int enter_room_x, enter_room_y; + +extern int player_dying; + +extern int checkpoint_x; +extern int checkpoint_y; + +extern int player_gems; + +extern int specialmessage; +extern int specialmessagetimer; + +extern int tele_select; + +void WritePlayerData(); +void ReadPlayerData(); + +extern int artifacts[]; + +void LoadingScreen(int part, float progress); +void SavingScreen(int part, float progress); + +void ThinLine(SDL_Surface *scr, int x1, int y1, int x2, int y2, Uint8 col); +float RandomDir(); + +void Arc(SDL_Surface *s, int x, int y, int r, float dir); + +extern SDL_Surface *artifact_spr; + +void VideoUpdate(); +void EndCycle(int n); + +extern int enter_pressed; + +extern int game_paused; + +extern int key_held[]; + +extern int training; +extern int show_ending; + +void DrawLevel(int off_x, int off_y, int hide_not_visited, int fog_of_war); +void DrawPlayer(int x, int y, int pl_dir, int pl_frm); +int GetNearestCheckpoint(int x, int y); +int dist(int x1, int y1, int x2, int y2); +#define K_UP 0 +#define K_DN 1 +#define K_LT 2 +#define K_RT 3 +#define K_SP 4 + +#endif diff --git a/src/mapgen.c b/src/mapgen.c new file mode 100755 index 0000000..b3411c5 --- /dev/null +++ b/src/mapgen.c @@ -0,0 +1,972 @@ +// +// mapgen.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include +#include + +#include +#include "save.h" +#include "levelblit.h" + +void NewLevel(); + +void SaveLevel(); + +int Generate(); + +int DoRepeat = 0; + +int place_of_power = 0; + +struct RoomConnection { + int x, y; + int x2, y2; + int c; + struct RoomConnection *n; +}; + +typedef struct { + int x, y; + int w, h; + int creator; + int visited; + int checkpoint; + int s_dist; + int connections; + int room_type; + int room_param; + int enemies; + struct RoomConnection *con; +} Room; + +typedef struct { + int w, h; + unsigned char *m; + int *r; + int totalRooms; + //Room *rooms; +} GameLevel; + +GameLevel map; +Room rooms[3000]; +int total_rooms = 0; + +int rdir = 0; + +int next_check = 30; + +int GetRoom(int x, int y); + +int rndnum(int max) +{ + return rand() % (max+1); +} + +int r_fails[4] = {0}; +int r_successes[4] = {0}; + +unsigned char floortiles[4] = {12, 18, 19, 20}; + +void WriteRoomData(Room *rm) +{ + struct RoomConnection *rt; + FWInt(rm->x); + FWInt(rm->y); + FWInt(rm->w); + FWInt(rm->h); + FWInt(rm->creator); + FWInt(rm->visited); + FWInt(rm->checkpoint); + FWInt(rm->s_dist); + FWInt(rm->connections); + FWInt(rm->room_type); + FWInt(rm->room_param); + rt = rm->con; + while (rt != NULL) { + FWInt(rt->x); + FWInt(rt->y); + FWInt(rt->x2); + FWInt(rt->y2); + FWInt(rt->c); + rt = rt->n; + } +} + +void ReadRoomData(Room *rm) +{ + int i; + struct RoomConnection *rt; + + rm->x = FRInt(); + rm->y = FRInt(); + rm->w = FRInt(); + rm->h = FRInt(); + rm->creator = FRInt(); + rm->visited = FRInt(); + rm->checkpoint = FRInt(); + rm->s_dist = FRInt(); + rm->connections = FRInt(); + rm->room_type = FRInt(); + rm->room_param = FRInt(); + + rm->con = NULL; + + rm->enemies = 0; + + for (i = 0; i < rm->connections; i++) { + rt = rm->con; + rm->con = malloc(sizeof(struct RoomConnection)); + rm->con->x = FRInt(); + rm->con->y = FRInt(); + rm->con->x2 = FRInt(); + rm->con->y2 = FRInt(); + rm->con->c = FRInt(); + rm->con->n = rt; + } +} + +void WriteMapData() +{ + int i; + + FWInt(map.w); + FWInt(map.h); + FWInt(map.totalRooms); + FWInt(place_of_power); + for (i = 0; i < map.w*map.h; i++) { + FWChar(map.m[i]); + FWInt(map.r[i]); + if ((i % 7447) == 7446) { + SavingScreen(0, (float)i / (float)(map.w*map.h)); + } + } + for (i = 0; i < map.totalRooms; i++) { + WriteRoomData(&rooms[i]); + if ((i % 85)==84) { + SavingScreen(1, (float)i / (float)map.totalRooms); + } + } +} + +void ReadMapData() +{ + int i; + + map.w = FRInt(); + map.h = FRInt(); + map.totalRooms = total_rooms = FRInt(); + place_of_power = FRInt(); + for (i = 0; i < map.w*map.h; i++) { + if ((i % 7447) == 7446) { + LoadingScreen(0, (float)i / (float)(map.w*map.h)); + } + map.m[i] = FRChar(); + map.r[i] = FRInt(); + } + LoadingScreen(0, 1); + for (i = 0; i < map.totalRooms; i++) { + ReadRoomData(&rooms[i]); + if ((i % 85)==84) { + LoadingScreen(1, (float)i / (float)map.totalRooms); + } + } + LoadingScreen(1, 1); +} + +int rndval(int a, int b) +{ + int temp; + + if (a == b) { + return a; + } + + if (b < a) { + temp = a; + a = b; + b = temp; + } + + temp = rndnum(b - a); + + return temp + a; +} + +void RandomGenerateMap() +{ + int trying = 1; + if (game_load) { + NewLevel(); + ReadMapData(); + } else { + NewLevel(); + while (trying) { + + trying = !Generate(); + } + } + //SaveLevel(); +} + +void NewLevel() +{ + int x, y; + unsigned char *map_p; + + map.w = 512; + map.h = 512; + + map.m = malloc(map.w * map.h * sizeof(unsigned char)); + map.r = malloc(map.w * map.h * sizeof(int)); + map_p = map.m; + + for (y = 0; y < map.h; y++) { + for (x = 0; x < map.w; x++) { + *(map_p++) = 17; + map.r[y*map.w+x] = -1; + } + } +} + +void DestroyDungeon() +{ + int i; + struct RoomConnection *c, *d; + + // Destroy map + free(map.m); + free(map.r); + + // Destroy rooms + for (i = 0; i < total_rooms; i++) { + c = rooms[i].con; + while (c != NULL) { + d = c; + c = c->n; + free(d); + } + } + total_rooms = 0; +} + +void ResetLevel() +{ + int x, y; + unsigned char *map_p; + + map.w = 512; + map.h = 512; + map_p = map.m; + + total_rooms = 0; + + rdir = 0; + + next_check = 30; + + for (y = 0; y < map.h; y++) { + for (x = 0; x < map.w; x++) { + *(map_p++) = 17; + map.r[y*map.w+x] = -1; + } + } +} + +void SaveLevel() +{ + int x, y, i; + SDL_Surface *map_surf; + char cs[2] = "."; + char rnum[5] = "0000"; + unsigned char ch; + unsigned char *map_p; + SDL_Color cpalette[4]; + Uint8 cl; + + map_surf = SDL_CreateRGBSurface(0, 4096, 4096, 8, 0, 0, 0, 0); + + map_p = map.m; + + cpalette[0].r = cpalette[0].g = cpalette[0].b = 0; + cpalette[1].r = cpalette[1].g = cpalette[1].b = 255; + cpalette[2].r = 255; cpalette[2].g = 0; cpalette[2].b = 255; + cpalette[3].r = 0; cpalette[3].g = 255; cpalette[3].b = 128; + + SDL_SetPalette(map_surf, SDL_LOGPAL | SDL_PHYSPAL, cpalette, 0, 4); + + for (y = 0; y < map.h; y++) { + for (x = 0; x < map.w; x++) { + ch = *(map_p++); + + if (IsSolid(ch)) + *cs = 4; + else + *cs = 5; + + if (ch == 17) + *cs = 0; + + cl = 1; + if (rooms[GetRoom(x, y)].room_type == 2) cl = 2; + if (rooms[GetRoom(x, y)].room_type == 3) cl = 3; + + draw_text_ex(x*8, y*8, cs, cl, map_surf); + } + } + for (i = 0; i < 3000; i++) { + sprintf(rnum, "%d", i); + draw_text_ex(rooms[i].x * 8, rooms[i].y * 8, rnum, 0, map_surf); + } + + SDL_SaveBMP(map_surf, "map.bmp"); +} + +void CreateRoomDimensions(int *w, int *h) +{ + *w = rndval(5, 12); + *h = rndval(5, 12); + + if (*w == 12) { + *w = rndval(12, 15); + } + if (*h == 12) { + *h = rndval(12, 15); + } +} + +void Put(int x, int y, unsigned char tile, int room) +{ + map.m[map.w*y+x] = tile; + map.r[map.w*y+x] = room; +} + +unsigned char Get(int x, int y) +{ + if (x < 0) return 17; + if (y < 0) return 17; + if (x >= map.w) return 17; + if (y >= map.h) return 17; + + return map.m[map.w*y+x]; +} + +int GetRoom(int x, int y) +{ + if (x < 0) return -1; + if (y < 0) return -1; + if (x >= map.w) return -1; + if (y >= map.h) return -1; + + return map.r[map.w*y+x]; +} + +int GetVisited(int x, int y) +{ + if (x < 0) return 0; + if (y < 0) return 0; + if (x >= map.w) return 0; + if (y >= map.h) return 0; + + return rooms[GetRoom(x, y)].visited; +} + +void Paint(int xp, int yp, int w, int h, char *fname) +{ + FILE *fp; + int x, y; + fp = fopen(fname, "rb"); + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + Put(x+xp, y+yp, fgetc(fp), GetRoom(x+xp, y+yp)); + } + } + fclose(fp); +} + +void DrawRoom(int place_x, int place_y, int room_w, int room_h, int room_id) +{ + int x, y, i; + int f_type; + + f_type = rand()%4; + // Corners + Put(place_x, place_y, 11, room_id); + Put(place_x + room_w - 1, place_y, 10, room_id); + Put(place_x, place_y + room_h - 1, 9, room_id); + Put(place_x + room_w - 1, place_y + room_h - 1, 8, room_id); + + // Walls + + for (i = 0; i < room_w - 2; i++) { + Put(place_x + 1 + i, place_y + room_h - 1, 4, room_id); + if (rand() % 16 == 0) Put(place_x + 1 + i, place_y + room_h - 1, 45 + (rand()%2)*4, room_id); + Put(place_x + 1 + i, place_y, 5, room_id); + if (rand() % 16 == 0) Put(place_x + 1 + i, place_y, 46 + (rand()%2)*4, room_id); + } + for (i = 0; i < room_h - 2; i++) { + Put(place_x + room_w - 1, place_y + 1 + i, 6, room_id); + if (rand() % 16 == 0) Put(place_x + room_w - 1, place_y + 1 + i, 47 + (rand()%2)*4, room_id); + Put(place_x, place_y + 1 + i, 7, room_id); + if (rand() % 16 == 0) Put(place_x, place_y + 1 + i, 48 + (rand()%2)*4, room_id); + } + + // Floor + + for (y = 0; y < room_h - 2; y++) { + for (x = 0; x < room_w - 2; x++) { + Put(place_x + 1 + x, place_y + 1 + y, floortiles[f_type], room_id); + } + } + + // Magic Tiles + + if ((room_id % 30) == 29) { + if (Get(place_x + 1 + rand()%(room_w-2), place_y + 1 + rand()%(room_h-2)) == floortiles[f_type]) { + Put(place_x + 1 + rand()%(room_w-2), place_y + 1 + rand()%(room_h-2), 28+rand()%3, room_id); + } + } + + // Save tiles + + if ((room_id % 25) == 20) { + x = place_x + 1 + rand()%(room_w-2); + y = place_y + 1 + rand()%(room_h-2); + if (Get(x, y) == floortiles[f_type]) { + Put(x, y, 31, room_id); + } + } + + // Summon tiles + if ((room_id % 75) == 48) { + x = place_x + 1 + rand()%(room_w-2); + y = place_y + 1 + rand()%(room_h-2); + if (Get(x, y) == floortiles[f_type]) { + Put(x, y, 32, room_id); + } + } + + // Compass tile + + if ((room_id % 20) == 19) { + x = place_x + 1 + rand()%(room_w-2); + y = place_y + 1 + rand()%(room_h-2); + if (Get(x, y) == floortiles[f_type]) { + Put(x, y, 53, room_id); + } + } + + // First room + if (room_id == 0) { + Paint(place_x+1, place_y+1, room_w-2, room_h-2, "dat/d/centre.loc"); + } + // Power object rooms + if ((room_id % 1000) == 499) { + Paint(place_x+1, place_y+1, room_w-2, room_h-2, "dat/d/weapon.loc"); + } + // Boss rooms + if ((room_id % 1000) == 999) { + Paint(place_x+1, place_y+1, room_w-2, room_h-2, "dat/d/bossroom.loc"); + } +} + +int NoRoomCollision(int place_x, int place_y, int room_w, int room_h) +{ + int x, y; + + if (place_x < 0) return 0; + if (place_y < 0) return 0; + if ((place_x+room_w) > map.w) return 0; + if ((place_y+room_h) > map.h) return 0; + + for (y = 0; y < room_h; y++) { + for (x = 0; x < room_w; x++) { + if (Get(place_x + x, place_y + y) != 17) return 0; + } + } + + return 1; +} + +void MakeConnect(int x, int y, int type) +{ + int nx, ny; + int d1, d2; + int room_1, room_2; + struct RoomConnection *rconnect; + + switch (type) { + case 0: + nx = x; + ny = y - 1; + d1 = 14; + d2 = 13; + break; + case 1: + nx = x; + ny = y + 1; + d1 = 13; + d2 = 14; + break; + case 2: + nx = x - 1; + ny = y; + d1 = 16; + d2 = 15; + break; + case 3: + nx = x + 1; + ny = y; + d1 = 15; + d2 = 16; + break; + default: + nx = 0; + ny = 0; + d1 = 0; + d2 = 0; + break; + } + + room_1 = GetRoom(x, y); + room_2 = GetRoom(nx, ny); + if ((room_1 % 1000) == 999) { + d1 = d1 - 13 + 21; + d2 = d2 - 13 + 38; + } else { + if ((room_2 % 1000) == 999) { + d1 = d1 - 13 + 38; + d2 = d2 - 13 + 21; + } + } + Put(x, y, d1, GetRoom(x, y)); + Put(nx, ny, d2, GetRoom(nx, ny)); + + rooms[room_1].connections++; + rconnect = rooms[room_1].con; + rooms[room_1].con = malloc(sizeof(struct RoomConnection)); + rooms[room_1].con->n = rconnect; + rooms[room_1].con->x = x; + rooms[room_1].con->y = y; + rooms[room_1].con->x2 = nx; + rooms[room_1].con->y2 = ny; + rooms[room_1].con->c = room_2; + + rooms[room_2].connections++; + rconnect = rooms[room_2].con; + rooms[room_2].con = malloc(sizeof(struct RoomConnection)); + rooms[room_2].con->n = rconnect; + rooms[room_2].con->x = nx; + rooms[room_2].con->y = ny; + rooms[room_2].con->x2 = x; + rooms[room_2].con->y2 = y; + rooms[room_2].con->c = room_1; + +} + +int SuitableConnection(int t) +{ + switch (t) { + case 4: + case 5: + case 6: + case 7: + + case 45: + case 46: + case 47: + case 48: + + case 49: + case 50: + case 51: + case 52: + return 1; + break; + + default: + break; + } + return 0; +} + +void NewRoom(int place_x, int place_y, int room_w, int room_h, int creator) +{ + int connect_points = 0; + int cplist_x[100], cplist_y[100], cplist_r[100], cplist_t[100]; + + int sr_cps = 0; + int sr_cp[100]; + + int sr_nps = 0; + int sr_np[100]; + + int i; + + // Draw this room + rooms[total_rooms].checkpoint = 0; + DrawRoom(place_x, place_y, room_w, room_h, total_rooms); + + rooms[total_rooms].x = place_x; + rooms[total_rooms].y = place_y; + + rooms[total_rooms].w = room_w; + rooms[total_rooms].h = room_h; + + rooms[total_rooms].room_type = 0; + rooms[total_rooms].room_param = 0; + + rooms[total_rooms].creator = creator; + + rooms[total_rooms].connections = 0; + rooms[total_rooms].con = NULL; + rooms[total_rooms].enemies = 0; + + rooms[total_rooms].visited = 0; + + rooms[total_rooms].s_dist = -1; + + if (total_rooms == 0) { + rooms[total_rooms].checkpoint = 1; + } + + + + total_rooms++; + + if (creator == -1) return; + + // Find connection points + + for (i = 0; i < room_w - 2; i++) { + if (SuitableConnection(Get(place_x + 1 + i, place_y - 1))) { + cplist_x[connect_points] = place_x + 1 + i; + cplist_y[connect_points] = place_y; + cplist_r[connect_points] = GetRoom(place_x + 1 + i, place_y - 1); + cplist_t[connect_points] = 0; + connect_points++; + } + + if (SuitableConnection(Get(place_x + 1 + i, place_y + room_h))) { + cplist_x[connect_points] = place_x + 1 + i; + cplist_y[connect_points] = place_y + room_h - 1; + cplist_r[connect_points] = GetRoom(place_x + 1 + i, place_y + room_h); + cplist_t[connect_points] = 1; + connect_points++; + } + } + for (i = 0; i < room_h - 2; i++) { + if (SuitableConnection(Get(place_x - 1, place_y + 1 + i))) { + cplist_x[connect_points] = place_x; + cplist_y[connect_points] = place_y + 1 + i; + cplist_r[connect_points] = GetRoom(place_x - 1, place_y + 1 + i); + cplist_t[connect_points] = 2; + connect_points++; + } + + if (SuitableConnection(Get(place_x + room_w, place_y + 1 + i))) { + cplist_x[connect_points] = place_x + room_w - 1; + cplist_y[connect_points] = place_y + 1 + i; + cplist_r[connect_points] = GetRoom(place_x + room_w, place_y + 1 + i); + cplist_t[connect_points] = 3; + connect_points++; + } + } + + for (i = 0; i < connect_points; i++) { + if (cplist_r[i] == creator) { + sr_cp[sr_cps++] = i; + } else { + sr_np[sr_nps++] = i; + } + } + + //printf("cps: %d room: %d\n", sr_cps, total_rooms); + + assert(sr_cps > 0); + + i = rndval(0, sr_cps-1); + MakeConnect(cplist_x[sr_cp[i]], cplist_y[sr_cp[i]], cplist_t[sr_cp[i]]); + + // one other connection (if we can) + if (sr_nps > 0) { + i = rndval(0, sr_nps-1); + MakeConnect(cplist_x[sr_np[i]], cplist_y[sr_np[i]], cplist_t[sr_np[i]]); + } + +} + +int AddChild(int room_id) +{ + Room r = rooms[room_id]; + int place_x = r.x; + int place_y = r.y; + int room_w = r.w; + int room_h = r.h; + int new_w, new_h, new_x, new_y; + int room_pos; + + int trying; + int attempts; + + + + trying = 1; + attempts = 0; + while (trying) { + attempts++; + + if (( (total_rooms+1) % 500)==0) { + new_w = 20; + new_h = 15; + } else { + CreateRoomDimensions(&new_w, &new_h); + } + + room_pos = (rdir++)%4; + + if (room_pos < 2) { + // vertical placement + new_x = rndval(place_x - (new_w - 3), place_x + (room_w - 3)); + if (room_pos == 0) { + new_y = place_y - new_h; + } else { + new_y = place_y + room_h; + } + } else { + // horiz placement + new_y = rndval(place_y - (new_h - 3), place_y + (room_h - 3)); + if (room_pos == 2) { + new_x = place_x - new_w; + } else { + new_x = place_x + room_w; + } + } + + if (NoRoomCollision(new_x, new_y, new_w, new_h)) { + //printf("SUCCESS\n"); + r_successes[room_pos]++; + NewRoom(new_x, new_y, new_w, new_h, room_id); + return 1; + } else { + //printf("FAIL %d\n", attempts); + r_fails[room_pos]++; + if (attempts > 20) return 0; + } + } + return 0; +} + +void RecurseSetDist() +{ + struct RoomConnection *rc; + int queue[10000]; + int q_top = 1; + int q_bot = 0; + int rooms_left = 3000; + int c_room; + queue[0] = 0; + + if (rooms_left % 100 == 0) { + LoadingScreen(1, 1.0 - ((float)rooms_left / 3000.0)); + } + + rooms[0].s_dist = 0; + + while ((rooms_left > 0)) { + c_room = queue[q_bot]; + q_bot++; + rooms_left--; + + rc = rooms[c_room].con; + + while (rc != NULL) { + //assert(qp < 3000); + if (rooms[rc->c].s_dist == -1) { + queue[q_top] = rc->c; + q_top++; + rooms[rc->c].s_dist = rooms[c_room].s_dist+1; + } + rc = rc->n; + } + } +} + +int RoomSize(int c_room) +{ + return sqrt(rooms[c_room].w*rooms[c_room].w + rooms[c_room].h*rooms[c_room].h); +} + +void MakeSpecialRooms() +{ + int i, j; + int c_tier; + int c_room; + int biggest_room_sz = 0; + int biggest_room_n = -1; + int rtyp[8] = {0}; + int ctyp; + int x, y; + + // Special rooms are: + // - Boss rooms @ 500, 1000, 1500, 2000, 2500, 3000 + // - Artifact rooms (biggest non-boss room of a given tier) + // Tiers: 5-9 10-14 15-19 20-24 25-29 30-34 35-39 40-44 + + // boss rooms + for (i = 0; i < 3; i++) { + c_room = i*1000+999; + rooms[c_room].room_type = 2; + rooms[c_room].room_param = i; + } + // power object rooms + for (i = 0; i < 3; i++) { + c_room = i*1000+499; + rooms[c_room].room_type = 5; + rooms[c_room].room_param = i; + } + + // artifact rooms + for (c_tier = 0; c_tier < 8; c_tier++) { + biggest_room_sz = 0; + for (c_room = 0; c_room < 3000; c_room++) { + if (rooms[c_room].room_type == 0) { + if (rooms[c_room].s_dist >= (c_tier*5+5)) { + if (rooms[c_room].s_dist <= (c_tier*5+9)) { + if (RoomSize(c_room) > biggest_room_sz) { + biggest_room_sz = RoomSize(c_room); + biggest_room_n = c_room; + } + } + } + } + } + rooms[biggest_room_n].room_type = 3; + + // pick a # + for (;;) { + ctyp = rand()%8; + if (rtyp[ctyp] == 0) { + rtyp[ctyp] = 1; + break; + } + } + + rooms[biggest_room_n].room_param = ctyp; + + //printf("Artifact room for tier %d is room %d (size %d), with artifact %d\n", c_tier, biggest_room_n, biggest_room_sz, ctyp); + } + + // place of power + // The room with the highest s_dist that is not of any other type + + for (i = 0; i < 3000; i++) { + if (rooms[i].s_dist > rooms[place_of_power].s_dist) { + if (rooms[i].room_type == 0) { + place_of_power = i; + } + } + } + + rooms[place_of_power].room_type = 6; + + // Now place some checkpoints in the remaining rooms + // Normally, we would have a checkpoint for every 30 + // rooms, BUT since we aren't using that method any + // more, we will simply use an equivalent--namely, to + // divide the map into an 8x8 grid and place one + // checkpoint per square + + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + j = -1; + for (i = 0; i < 20; i++) { + j = GetRoom(rand() % 64 + x * 64, rand() % 64 + y * 64); + + if (j >= 0) { + if (rooms[j].room_type == 0) { + Put(rooms[j].x + rooms[j].w / 2, rooms[j].y + rooms[j].h / 2, 25, j); + rooms[j].checkpoint = 1; + break; + } + } + } + } + } + + next_check--; +} + +int Generate() +{ + int attempts = 0; + int i; + int correct_dist = 0; + int maxdist = 0; + rdir = rand()%4; + NewRoom(map.w / 2 - 20 / 2, map.h / 2 - 15 / 2, 20, 15, -1); + + for (attempts = 0; attempts < 100000; attempts++) { + assert(map.w == 512); + AddChild(rndval(rndval(0, total_rooms-1), total_rooms-1)); + if (total_rooms % 100 == 99) { + LoadingScreen(0, (float)total_rooms / 3000.0); + } + if (total_rooms == 3000) break; + } + + if ((total_rooms < 3000)||(DoRepeat == 1)) { + DoRepeat = 0; + ResetLevel(); + return 0; + } + + RecurseSetDist(); + + for (i = 0; i < 3000; i++) { + if (rooms[i].s_dist > maxdist) { + maxdist = rooms[i].s_dist; + } + + if (rooms[i].s_dist >= 50) { + correct_dist = 1; + } + } + + if (correct_dist == 0) { + //printf("Dist fail (only %d)\n", maxdist); + DoRepeat = 0; + ResetLevel(); + return 0; + } + + //printf("Rooms: %d\n", total_rooms); + + MakeSpecialRooms(); + + map.totalRooms = total_rooms; + return 1; +} + diff --git a/src/mapgen.h b/src/mapgen.h new file mode 100755 index 0000000..aa1c4d4 --- /dev/null +++ b/src/mapgen.h @@ -0,0 +1,74 @@ +// +// mapgen.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// Exposes mapgen.c functionality and types + +#ifndef MAPGEN_H +#define MAPGEN_H + +struct RoomConnection { + int x, y; + int x2, y2; + int c; + struct RoomConnection *n; +}; + +typedef struct { + int x, y; + int w, h; + int creator; + int visited; + int checkpoint; + int s_dist; + int connections; + int room_type; + int room_param; + int enemies; + struct RoomConnection *con; +} Room; + +typedef struct { + int w, h; + unsigned char *m; + int *r; + int totalRooms; + //Room *rooms; +} GameLevel; + +extern GameLevel map; +extern Room rooms[3000]; + +void RandomGenerateMap(); + +void Put(int x, int y, unsigned char tile, int room); +unsigned char Get(int x, int y); +int GetRoom(int x, int y); +int GetVisited(int x, int y); +extern int place_of_power; + +void WriteMapData(); +void ReadMapData(); + +void DestroyDungeon(); + +void Paint(int xp, int yp, int w, int h, char *fname); + +#endif diff --git a/src/save.c b/src/save.c new file mode 100755 index 0000000..d7818d9 --- /dev/null +++ b/src/save.c @@ -0,0 +1,169 @@ +// +// save.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +#include +#include +#include +#include +#include +#include + +#include "levelblit.h" +#include "mapgen.h" +#include "demon.h" +#include "tiles.h" + +gzFile Filefp; +int game_load = 0; + +unsigned char lchar; +int fpos = 0; + +void FWChar(unsigned char i) +{ + unsigned char c; + c = i; + c ^= 0x55; + c ^= fpos & 0xFF; + lchar = c; + fpos++; + gzputc(Filefp, c); +} + +unsigned char FRChar() +{ + unsigned char c; + c = gzgetc(Filefp); + c ^= 0x55; + c ^= fpos & 0xFF; + + lchar = c; + fpos++; + return c; +} + +void FWInt(int val) +{ + int i, s; + i = abs(val); + s = (val >= 0) ? 0 : 1; + + FWChar((i & 0xFF) >> 0); + FWChar((i & 0xFF00) >> 8); + FWChar((i & 0xFF0000) >> 16); + FWChar((i & 0xFF000000) >> 24); + + FWChar(s); +} + +void FWFloat(float i) +{ + int num; + int frac; + + num = (int)(floorf(i)); + FWInt(num); + frac = (int)((i - (float)num)*2147483647.0); + + FWInt(frac); +} + +int FRInt() +{ + int val; + int i, s; + + i = FRChar(); + i |= FRChar() << 8; + i |= FRChar() << 16; + i |= FRChar() << 24; + s = FRChar(); + val = i * (s?-1:1); + + return val; +} + +float FRFloat() +{ + float i; + int num; + int frac; + + double f_frac; + + num = FRInt(); + frac = FRInt(); + + f_frac = (double)frac / 2147483647.0; + + i = (float)num + (float)f_frac; + return i; +} + +void SaveGame(char *filename) +{ + lchar = 0x7c; + fpos = 0; + + Filefp = gzopen(filename, "wb9"); + FWChar(0x7C); + WriteMapData(); + WriteCreatureData(); + WritePlayerData(); + + gzclose(Filefp); +} + +void LoadGame(char *filename) +{ + unsigned char parity; + fpos = 0; + lchar = 0x7c; + + Filefp = gzopen(filename, "rb"); + parity = FRChar(); + if (parity != 0x7C) { + fprintf(stderr, "Parity byte in error (%x != 0x7C)\nAborting\n", parity); + exit(2); + } + game_load = 1; +} + +void CloseFile() +{ + gzclose(Filefp); +} + +void DoSaveGame() +{ + SavingScreen(0, 0.0); + SaveGame("SaveFile.sav"); +} + +int IsSaveFile() +{ + FILE *fp; + if ((fp = fopen("SaveFile.sav", "rb")) != NULL) { + fclose(fp); + return 1; + } + return 0; +} diff --git a/src/save.h b/src/save.h new file mode 100755 index 0000000..e721764 --- /dev/null +++ b/src/save.h @@ -0,0 +1,44 @@ +// +// save.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// save.c header file + +#ifndef SAVE_H +#define SAVE_H + +void DoSaveGame(); + +void FWInt(int val); +void FWChar(unsigned char i); +void FWFloat(float i); +float FRFloat(); +unsigned char FRChar(); +int FRInt(); + +extern int game_load; + +void SaveGame(char *); +void LoadGame(char *); +void CloseFile(); + +int IsSaveFile(); + +#endif diff --git a/src/tiles.c b/src/tiles.c new file mode 100755 index 0000000..ce18ab9 --- /dev/null +++ b/src/tiles.c @@ -0,0 +1,104 @@ +// +// tiles.c +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// tile data + +struct TileInfo { + int Is_Solid; + int Is_Special; + int Is_Passage; + int Is_Wall; + int Type; +}; + +struct TileInfo TileData[57] = { + {1, 0, 0, 1, 0}, // 0 + {1, 0, 0, 1, 0}, // 1 + {1, 0, 0, 1, 0}, // 2 + {1, 0, 0, 1, 0}, // 3 + {1, 0, 0, 1, 0}, // 4 + {1, 0, 0, 1, 0}, // 5 + {1, 0, 0, 1, 0}, // 6 + {1, 0, 0, 1, 0}, // 7 + {1, 0, 0, 1, 0}, // 8 + {1, 0, 0, 1, 0}, // 9 + {1, 0, 0, 1, 0}, // 10 + {1, 0, 0, 1, 0}, // 11 + {0, 0, 0, 0, 1}, // 12 + {1, 0, 1, 0, 2}, // 13 + {1, 0, 1, 0, 2}, // 14 + {1, 0, 1, 0, 2}, // 15 + {1, 0, 1, 0, 2}, // 16 + {1, 0, 0, 0, 3}, // 17 + {0, 0, 0, 0, 1}, // 18 + {0, 0, 0, 0, 1}, // 19 + {0, 0, 0, 0, 1}, // 20 + {1, 0, 0, 1, 4}, // 21 + {1, 0, 0, 1, 4}, // 22 + {1, 0, 0, 1, 4}, // 23 + {1, 0, 0, 1, 4}, // 24 + {0, 1, 0, 0, 5}, // 25 + {0, 1, 0, 0, 6}, // 26 + {0, 0, 0, 0, 7}, // 27 + {0, 1, 0, 0, 8}, // 28 + {0, 1, 0, 0, 8}, // 29 + {0, 1, 0, 0, 8}, // 30 + {0, 1, 0, 0, 8}, // 31 + {0, 1, 0, 0, 8}, // 32 + {0, 0, 0, 0, 1}, // 33 + {1, 0, 0, 0, 0}, // 34 + {1, 0, 0, 0, 0}, // 35 + {1, 0, 0, 0, 0}, // 36 + {1, 0, 0, 0, 0}, // 37 + {1, 0, 0, 1, 4}, // 38 + {1, 0, 0, 1, 4}, // 39 + {1, 0, 0, 1, 4}, // 40 + {1, 0, 0, 1, 4}, // 41 + {0, 0, 0, 0, 8}, // 42 + {0, 0, 0, 0, 1}, // 43 + {0, 0, 0, 0, 1}, // 44 + {1, 0, 0, 1, 0}, // 45 + {1, 0, 0, 1, 0}, // 46 + {1, 0, 0, 1, 0}, // 47 + {1, 0, 0, 1, 0}, // 48 + {1, 0, 0, 1, 0}, // 49 + {1, 0, 0, 1, 0}, // 50 + {1, 0, 0, 1, 0}, // 51 + {1, 0, 0, 1, 0}, // 52 + {0, 1, 0, 0, 8}, // 53 + {0, 0, 0, 0, 1}, // 54 + {0, 0, 0, 0, 1}, // 55 + {0, 0, 0, 0, 1} // 56 +}; + +unsigned char automap_cols[10] = { + 140, // 0: Room wall + 192, // 1: Floor + 100, // 2: Passage + 255, // 3: Void + 120, // 4: Locked passage + 230, // 5: Checkpoint + 240, // 6: Closed chest + 200, // 7: Open chest + 230, // 8: Magic tile + 0 // 9: -- + +}; diff --git a/src/tiles.h b/src/tiles.h new file mode 100755 index 0000000..36e6e6b --- /dev/null +++ b/src/tiles.h @@ -0,0 +1,40 @@ +// +// tiles.h +// +// Copyright 2007, 2008 Lancer-X/ASCEAI +// +// This file is part of Meritous. +// +// Meritous is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Meritous is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Meritous. If not, see . +// + +// tile data + +#ifndef TILES_H +#define TILES_H + +struct TileInfo { + int Is_Solid; + int Is_Special; + int Is_Passage; + int Is_Wall; + int Type; +}; + +extern struct TileInfo TileData[48]; + +extern unsigned char automap_cols[10]; + +#endif +