feat: add native hell expedition
Add a painting entrance, Freedoom combat and Doom-style HUD. Persist the one-time clear reward in signed saves without changing file sizes. Include native regression checks and synchronized game/launcher builds.
This commit is contained in:
parent
7e4f7d7be1
commit
ce5121debd
54 changed files with 3229 additions and 4 deletions
|
|
@ -23,6 +23,14 @@ Running setup again verifies existing weights without downloading them again.
|
||||||
Complete offline release ZIPs already contain the model and need no download.
|
Complete offline release ZIPs already contain the model and need no download.
|
||||||
See [Fortune AI](docs/FORTUNE_AI.md) for details and hardware notes.
|
See [Fortune AI](docs/FORTUNE_AI.md) for details and hardware notes.
|
||||||
|
|
||||||
|
## Hell Expedition
|
||||||
|
|
||||||
|
The large hell painting opposite the first-floor bar opens an assembly-native FPS expedition:
|
||||||
|
Freedoom enemies and weapons, a blue-key gate, two secrets, lava and a final
|
||||||
|
boss, accompanied by the existing Mick Gordon soundtrack. First clear awards
|
||||||
|
$6,660; death/retry preserves the casino bank. See
|
||||||
|
[controls, assets and verification](docs/HELL_EXPEDITION.md).
|
||||||
|
|
||||||
## Native Entrance And Character Scenes
|
## Native Entrance And Character Scenes
|
||||||
|
|
||||||
The graphical startup now fades from Eblan Games into a first-person opening:
|
The graphical startup now fades from Eblan Games into a first-person opening:
|
||||||
|
|
|
||||||
30
assets/hell/COPYING.txt
Normal file
30
assets/hell/COPYING.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
Copyright © 2001-2024
|
||||||
|
Contributors to the Freedoom project. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the Freedoom project nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS
|
||||||
|
IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||||
|
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
For a list of contributors to the Freedoom project, see the file
|
||||||
|
CREDITS.
|
||||||
205
assets/hell/CREDITS.txt
Normal file
205
assets/hell/CREDITS.txt
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
--Phase 1--
|
||||||
|
|
||||||
|
Title: "Fight for Freedom" by Korp, based on "Fountain Dance" by Tyler "Picklehammer" Pantella
|
||||||
|
Intermission: "YOU LIVE!!!!" by Stratos
|
||||||
|
Victory: "Distance" by Korp
|
||||||
|
Bunny: "Impactean Welcome Party" by Goji
|
||||||
|
|
||||||
|
Episode 1 - Outpost Outbreak
|
||||||
|
E1M1: "Stanky Leg Specialist" by Lola "BlueWorrior" Harvey
|
||||||
|
E1M2: "Look At Me I'm Underhalls Too" by Mark "TheMisterCat" McGill-Smith
|
||||||
|
E1M3: "Alone with Colors" by Kevin "Velvetic" Martins & Jonathan "julnen" Martins
|
||||||
|
E1M9: "Hidden Between Spades" by Korp, remix of dave3d42.mid by jute
|
||||||
|
E1M4: "Flood the City" by Kevin "Velvetic" Martins & Jonathan "julnen" Martins
|
||||||
|
E1M5: "The Raging Sun" by Kevin "Velvetic" Martins & Jonathan "julnen" Martins, modified by Goji
|
||||||
|
E1M6: "Oxidine" by Korp, remix of dave3d35.mid by jute
|
||||||
|
E1M7: "Totally Surrounded" by Josephus "DH4050" Astartes
|
||||||
|
E1M8: "On the Next Day" by Kevin "Velvetic" Martins
|
||||||
|
|
||||||
|
Episode 2 - Military Labs
|
||||||
|
E2M1: "Moonstone" by Korp
|
||||||
|
E2M2: "Voidgaze" by Korp
|
||||||
|
E2M3: "Workout" by Korp
|
||||||
|
E2M4: "Diodine" by Korp
|
||||||
|
E2M5: "Reminiscence" by Korp
|
||||||
|
E2M9: "Nightstorm" by Korp
|
||||||
|
E2M6: "Concept of Technology" by Korp
|
||||||
|
E2M7: "Ribcage" by Korp
|
||||||
|
E2M8: "Treble Dissension" by Korp
|
||||||
|
|
||||||
|
Episode 3 - Event Horizon
|
||||||
|
E3M1: "Intrare In Infernum" by Lola "BlueWorrior" Harvey
|
||||||
|
E3M2: "The Long Road" by Lola "BlueWorrior" Harvey
|
||||||
|
E3M3: "Wrought Havoc" by Lola "BlueWorrior" Harvey
|
||||||
|
E3M4: "Cautious Progress" by Lola "BlueWorrior" Harvey
|
||||||
|
E3M5: "Scorned by Flesh" by Lola "BlueWorrior" Harvey
|
||||||
|
E3M6: "Soul Banisher" by Lola "BlueWorrior" Harvey
|
||||||
|
E3M9: "No-Clip to the End" by Lola "BlueWorrior" Harvey
|
||||||
|
E3M7: "Ultamatum Infurnus" by Lola "BlueWorrior" Harvey
|
||||||
|
E3M8: "The Zenith" by Lola "BlueWorrior" Harvey
|
||||||
|
|
||||||
|
Episode 4 - Double Impact
|
||||||
|
E4M1: "Liberate the Stars" by continuum.mid
|
||||||
|
E4M2: "Fight Harder... To Obtain a Better Victory!" by Proxy-MIDI
|
||||||
|
E4M9: "The Deepest Secrets" by continuum.mid
|
||||||
|
E4M3: "Space Spectre" by Matzu
|
||||||
|
E4M4: "Deliberate Concealment" by continuum.mid
|
||||||
|
E4M5: "Main Code" by Korp
|
||||||
|
E4M6: "Andromeda" by ZeMystic
|
||||||
|
E4M7: "Ambiguity" by Korp
|
||||||
|
E4M8: "Vs. The Assault Quindecapod" by continuum.mid
|
||||||
|
|
||||||
|
|
||||||
|
--Phase 2--
|
||||||
|
|
||||||
|
Title: "Horizon's Call" by Korp
|
||||||
|
Intermission: "FTW You 100" by Lola "BlueWorrior" Harvey
|
||||||
|
Story: dave3d07.mid by jute
|
||||||
|
|
||||||
|
Cluster 1
|
||||||
|
MAP01: "Not My First Rodeo..." by Lola "BlueWorrior" Harvey
|
||||||
|
MAP02: "Dark Underworld" by Lola "BlueWorrior" Harvey
|
||||||
|
MAP03: "Demon Dance" by Discoholic
|
||||||
|
MAP04: "Indigo" by Korp
|
||||||
|
MAP05: "Under Maintenance" by Lola "BlueWorrior" Harvey
|
||||||
|
MAP06: "Refrigeration" by Korp, based on "Dark Trail" by Tyler "Picklehammer" Pantella
|
||||||
|
MAP07: "Fun is Infinite at AGM" by continuum.mid
|
||||||
|
MAP08: "Syntax Error" by Lola "BlueWorrior" Harvey
|
||||||
|
MAP09: "Terrible Secrets" by Lola "BlueWorrior" Harvey
|
||||||
|
MAP10: "Soviet Porno" by Jeremy "Vandalorian" Emerson
|
||||||
|
MAP11: "Undergrowth" by Korp, remix of dave3d01.mid by jute
|
||||||
|
|
||||||
|
Cluster 2
|
||||||
|
MAP12: "Sewers" by Discoholic, remix of "Wasteland Scavengers" by Samuel "Blastfrog" Oliver
|
||||||
|
MAP13: "Fear" by Discoholic
|
||||||
|
MAP14: "About the Summer" by Kevin "Velvetic" Martins & Jonathan "julnen" Martins
|
||||||
|
MAP15: "Backpack of Shells" by Lola "BlueWorrior" Harvey
|
||||||
|
MAP31: "The Machine" by Lola "BlueWorrior" Harvey
|
||||||
|
MAP32: "Black Mountain" by Kevin "Velvetic" Martins & Jonathan "julnen" Martins
|
||||||
|
MAP16: "Reflection" by Scragadelic
|
||||||
|
MAP17: "Mow Them Down!" by Lola "BlueWorrior" Harvey
|
||||||
|
MAP18: "Realistic Approach" by Kevin "Velvetic" Martins
|
||||||
|
MAP19: "Deceit" by Julian "Julian Hope" Aubourg, modified by m
|
||||||
|
MAP20: "Beasts of Horizon" by Josephus "DH4050" Astartes
|
||||||
|
|
||||||
|
Cluster 3
|
||||||
|
MAP21: "Rough Landing" by Korp, remix of dave3d08.mid by jute
|
||||||
|
MAP22: "Alien Waltz" by Korp
|
||||||
|
MAP23: "Within the Trilo's Den" by MoonDeLaAxel and Korp
|
||||||
|
MAP24: "Space's Despair" by Korp, remix of "Balancing Act" by Andrew Bassett
|
||||||
|
MAP25: "I Sought Wisdom in the Chalice But There Was None" by Jeremy "Vandalorian" Emerson
|
||||||
|
MAP26: "Stormer" by Korp, originally by Samuel "Blastfrog" Oliver
|
||||||
|
MAP27: "Dreamcatcher (Black and Blue)" by Kevin "Velvetic" Martins & Jonathan "julnen" Martins
|
||||||
|
MAP28: "Skull Underfoot" by Lola "BlueWorrior" Harvey
|
||||||
|
MAP29: "Mists Over the Mire" by Tyler "Picklehammer" Pantella
|
||||||
|
MAP30: "Devoured by the Jaws of Defeat" by Korp
|
||||||
|
|
||||||
|
|
||||||
|
--FreeDM--
|
||||||
|
|
||||||
|
Title: "My Home, The Colosseum" by Goji
|
||||||
|
Intermission: "Burn the Bodies" by Tyler "Picklehammer" Pantella
|
||||||
|
Story: "Geoff's Vinyl Collection" by MoonDeLaAxel, remix of "Why?" by Joshua "wastedjamacan" Masek
|
||||||
|
|
||||||
|
DM01: "Zero Fort" by Ralph "Ralphis" Vickers
|
||||||
|
DM02: "Descent from Grace" by Lola "BlueWorrior" Harvey
|
||||||
|
DM03: "Dynamite!" by Korp
|
||||||
|
DM04: "Pit" by Anthony "Ajanddino" Pierce
|
||||||
|
DM05: "Music for Freedoom 9" by Jeremy "Vandalorian" Emerson
|
||||||
|
DM06: "Tunnel" by Anthony "Ajanddino" Pierce
|
||||||
|
DM07: "I Fell Off The Stairs" by Korp
|
||||||
|
DM08: "Jump Is Not Allowed!" by Korp
|
||||||
|
|
||||||
|
DM09: "Method to My Madness" by Matias "jupiter_ex" Roldan
|
||||||
|
DM10: "Paranoia" by Tyler "Picklehammer" Pantella
|
||||||
|
DM11: "Death's Embrace" by Tyler "Picklehammer" Pantella
|
||||||
|
DM12: "Parallax" by Korp
|
||||||
|
DM13: "Shut It" by Korp
|
||||||
|
DM14: "The Heroic Battle" by Tyler "Picklehammer" Pantella
|
||||||
|
DM15: "Neat" by Ralph "Ralphis" Vickers
|
||||||
|
DM16: "Deathmatch Construct" by Korp
|
||||||
|
|
||||||
|
DM17: "Panic" by Anthony "Ajanddino" Pierce
|
||||||
|
DM18: "Metadata" by Korp
|
||||||
|
DM19: "Tap Water" by Korp, remix of dave3d27.mid by jute
|
||||||
|
DM20: "Quirkspitter" by Korp, remix of dave3d16.mid by jute
|
||||||
|
DM21: "Friendly Fire" by Korp
|
||||||
|
DM22: "Red Foot" by Kevin "Velvetic" Martins and Simone Alauk
|
||||||
|
DM23: "Shells" by Ospaggi
|
||||||
|
DM24: "Constructive Criticism" by Matias "jupiter_ex" Roldan
|
||||||
|
|
||||||
|
DM25: "Shoot 'em All" by rostuhan
|
||||||
|
DM26: "World of Pain" by Josephus "DH4050" Astartes
|
||||||
|
DM27: "White Lights" by Kevin "Velvetic" Martins and Jonathan "julnen" Martins
|
||||||
|
DM28: "Bobby Sent Me" by Ralph "Ralphis" Vickers
|
||||||
|
DM29: "SevenFour" by Ralph "Ralphis" Vickers
|
||||||
|
DM30: "Freedom for Your Shooting Skills" by rostuhan
|
||||||
|
DM31: "The Syntax of Time" by Kevin "Velvetic" Martins
|
||||||
|
DM32: "Bullet Hell" by rostuhan
|
||||||
|
|
||||||
|
|
||||||
|
--Previously Featured in 0.12.1--
|
||||||
|
|
||||||
|
Phase 1 Title: "Fountain Dance" by Tyler "Picklehammer" Pantella
|
||||||
|
Phase 1 OPL Title: "I Hate Fredrik Johansson" by Toby "Tobester" Collins Jr. (moved to FreeDM Title, not removed)
|
||||||
|
Phase 1 Intermission: "Music for Freedoom 10" by Jeremy "Vandalorian" Emerson
|
||||||
|
Victory: "Ominous Theme" by Jared "BlackJar72" Blackburn
|
||||||
|
Bunny: "Look At The Bunnies" by RestlessRodent
|
||||||
|
E1M1: e1m1.mid by Ralph "Ralphis" Vickers
|
||||||
|
E1M3: "Mortal" by Jeremy "Vandalorian" Emerson
|
||||||
|
E1M5: "sit on a bench 'n release that tensh'n" by Jazz "jmickle" Mickle
|
||||||
|
E1M6: dave3d35.mid by jute
|
||||||
|
E1M7: "Sewers" by Discoholic, remix of "Wasteland Scavengers" by Samuel "Blastfrog" Oliver (moved to MAP12, not removed)
|
||||||
|
E1M9: "The Raging Sun" by Kevin "Velvetic" Martins and Jonathan "julnen" Martins (moved to E1M5, not removed)
|
||||||
|
E2M1: dave3d38.mid by jute
|
||||||
|
E2M2: "Animal Skin" by Kevin "Velvetic" Martins and Jonathan "julnen" Martins
|
||||||
|
E2M3: "Alone with Colors" by Kevin "Velvetic" Martins and Jonathan "julnen" Martins (moved to E1M3, not removed)
|
||||||
|
E2M4: "Let 'em Burn" by Kevin "Velvetic" Martins
|
||||||
|
E2M5: "Music for Freedoom 1" by Jeremy "Vandalorian" Emerson
|
||||||
|
E2M6: "sit on a bench 'n release that tensh'n" by Jazz "jmickle" Mickle
|
||||||
|
E2M7: "Music for Freedoom 3" by Jeremy "Vandalorian" Emerson
|
||||||
|
E2M8: "Pit" by Anthony "Ajanddino" Pierce (moved to DM04, not removed)
|
||||||
|
E2M9: dave3d34.mid by jute
|
||||||
|
E3M9: "Lipstick" by Jeremy "Vandalorian" Emerson
|
||||||
|
|
||||||
|
Phase 2 + FreeDM Title: "Warpath" by Tyler "Picklehammer" Pantella
|
||||||
|
Phase 2 Intermission: "Burn the Bodies" by Tyler "Picklehammer" Pantella
|
||||||
|
MAP04: "Soviet Porno" by Jeremy "Vandalorian" Emerson (moved to MAP10, not removed)
|
||||||
|
MAP06: "Dark Trail" by Tyler "Picklehammer" Pantella
|
||||||
|
MAP07: "Riders of Hell" by Tyler "Picklehammer" Pantella
|
||||||
|
MAP10: "Death's Embrace" by Tyler "Picklehammer" Pantella (moved to DM11, not removed)
|
||||||
|
MAP11: dave3d02.mid by jute
|
||||||
|
MAP12: Doom2Alt.mid by Bradley "BobFromReboot" Lavigne
|
||||||
|
MAP20: dave3d05.mid by jute
|
||||||
|
MAP21: "Violence Culture" by Jeremy "Vandalorian" Emerson
|
||||||
|
MAP22: "Mists Over the Mire" by Tyler "Picklehammer" Pantella (moved to MAP29, not removed)
|
||||||
|
MAP23: dave3d09.mid by jute
|
||||||
|
MAP24: "Balancing Act" by Andrew Bassett
|
||||||
|
MAP26: "Crawl" by Jeremy "Vandalorian" Emerson
|
||||||
|
MAP29: freedoom.mid by Varis Alpha
|
||||||
|
MAP30: "Paranoia" by Tyler "Picklehammer" Pantella (moved to DM10, not removed)
|
||||||
|
|
||||||
|
DM03: "Music for Freedoom 2" by Jeremy "Vandalorian" Emerson
|
||||||
|
DM04: "Rush" by Kevin "Velvetic" Martins and Simone Alauk
|
||||||
|
DM05: map01.mid by Ralph "Ralphis" Vickers
|
||||||
|
DM06: "The Heroic Battle" by Tyler "Picklehammer" Pantella (moved to DM14, not removed)
|
||||||
|
DM07: dave3d30.mid by jute
|
||||||
|
DM08: Dave3d13.mid by jute
|
||||||
|
DM10: Doom1.mid by Bradley "BobFromReboot" Lavigne
|
||||||
|
DM11: "Spamhambled In Space" by Mark "TheMisterCat" McGill-Smith
|
||||||
|
DM12: dave3d24.mid by jute
|
||||||
|
DM13: e1m1.mid by Ralph "Ralphis" Vickers
|
||||||
|
DM14: "Munt Battle" by Mark "TheMisterCat" McGill-Smith
|
||||||
|
DM16: dave3d02.mid by jute
|
||||||
|
DM17: d_stlks3.mid by Vincent "Vicious" Fong
|
||||||
|
DM18: dave3d06.mid by jute
|
||||||
|
DM19: dave3d27.mid by jute
|
||||||
|
DM20: dave3d16.mid by jute
|
||||||
|
DM21: dave3d12.mid by jute
|
||||||
|
DM23: dave3d17.mid by jute
|
||||||
|
DM25: dave3d20.mid by jute
|
||||||
|
DM26: dave3d09.mid by jute
|
||||||
|
DM27: "Music for Freedoom 7" by Jeremy "Vandalorian" Emerson
|
||||||
|
DM30: dave3d01.mid by jute
|
||||||
|
DM32: "Munt Battle" by Mark "TheMisterCat" McGill-Smith
|
||||||
|
|
||||||
BIN
assets/hell/boss.bin
Normal file
BIN
assets/hell/boss.bin
Normal file
Binary file not shown.
BIN
assets/hell/demon.bin
Normal file
BIN
assets/hell/demon.bin
Normal file
Binary file not shown.
BIN
assets/hell/digits.bin
Normal file
BIN
assets/hell/digits.bin
Normal file
Binary file not shown.
BIN
assets/hell/dsdoropn.wav
Normal file
BIN
assets/hell/dsdoropn.wav
Normal file
Binary file not shown.
BIN
assets/hell/dsfirsht.wav
Normal file
BIN
assets/hell/dsfirsht.wav
Normal file
Binary file not shown.
BIN
assets/hell/dsitemup.wav
Normal file
BIN
assets/hell/dsitemup.wav
Normal file
Binary file not shown.
BIN
assets/hell/dspistol.wav
Normal file
BIN
assets/hell/dspistol.wav
Normal file
Binary file not shown.
BIN
assets/hell/dsplpain.wav
Normal file
BIN
assets/hell/dsplpain.wav
Normal file
Binary file not shown.
BIN
assets/hell/dspodth1.wav
Normal file
BIN
assets/hell/dspodth1.wav
Normal file
Binary file not shown.
BIN
assets/hell/dspopain.wav
Normal file
BIN
assets/hell/dspopain.wav
Normal file
Binary file not shown.
BIN
assets/hell/dssgcock.wav
Normal file
BIN
assets/hell/dssgcock.wav
Normal file
Binary file not shown.
BIN
assets/hell/dsshotgn.wav
Normal file
BIN
assets/hell/dsshotgn.wav
Normal file
Binary file not shown.
BIN
assets/hell/dstelept.wav
Normal file
BIN
assets/hell/dstelept.wav
Normal file
Binary file not shown.
BIN
assets/hell/faces.bin
Normal file
BIN
assets/hell/faces.bin
Normal file
Binary file not shown.
BIN
assets/hell/fire.bin
Normal file
BIN
assets/hell/fire.bin
Normal file
Binary file not shown.
BIN
assets/hell/hud.bin
Normal file
BIN
assets/hell/hud.bin
Normal file
Binary file not shown.
BIN
assets/hell/imp.bin
Normal file
BIN
assets/hell/imp.bin
Normal file
Binary file not shown.
BIN
assets/hell/impacts.bin
Normal file
BIN
assets/hell/impacts.bin
Normal file
Binary file not shown.
BIN
assets/hell/items.bin
Normal file
BIN
assets/hell/items.bin
Normal file
Binary file not shown.
169
assets/hell/manifest.json
Normal file
169
assets/hell/manifest.json
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
{
|
||||||
|
"version": "0.13.0",
|
||||||
|
"url": "https://github.com/freedoom/freedoom/releases/download/v0.13.0/freedoom-0.13.0.zip",
|
||||||
|
"archive_sha256": "3f9b264f3e3ce503b4fb7f6bdcb1f419d93c7b546f4df3e874dd878db9688f59",
|
||||||
|
"packs": {
|
||||||
|
"soldier": [
|
||||||
|
"POSSA1",
|
||||||
|
"POSSB1",
|
||||||
|
"POSSC1",
|
||||||
|
"POSSD1",
|
||||||
|
"POSSE1",
|
||||||
|
"POSSF1",
|
||||||
|
"POSSG1",
|
||||||
|
"POSSH0",
|
||||||
|
"POSSI0",
|
||||||
|
"POSSJ0",
|
||||||
|
"POSSK0",
|
||||||
|
"POSSL0"
|
||||||
|
],
|
||||||
|
"demon": [
|
||||||
|
"SARGA1",
|
||||||
|
"SARGB1",
|
||||||
|
"SARGC1",
|
||||||
|
"SARGD1",
|
||||||
|
"SARGE1",
|
||||||
|
"SARGF1",
|
||||||
|
"SARGH1",
|
||||||
|
"SARGI0",
|
||||||
|
"SARGJ0",
|
||||||
|
"SARGK0",
|
||||||
|
"SARGL0",
|
||||||
|
"SARGN0"
|
||||||
|
],
|
||||||
|
"imp": [
|
||||||
|
"TROOA1",
|
||||||
|
"TROOB1",
|
||||||
|
"TROOC1",
|
||||||
|
"TROOD1",
|
||||||
|
"TROOE1",
|
||||||
|
"TROOF1",
|
||||||
|
"TROOH1",
|
||||||
|
"TROOI0",
|
||||||
|
"TROOJ0",
|
||||||
|
"TROOK0",
|
||||||
|
"TROOL0",
|
||||||
|
"TROOM0"
|
||||||
|
],
|
||||||
|
"boss": [
|
||||||
|
"BOSSA1",
|
||||||
|
"BOSSB1",
|
||||||
|
"BOSSC1",
|
||||||
|
"BOSSD1",
|
||||||
|
"BOSSE1",
|
||||||
|
"BOSSF1",
|
||||||
|
"BOSSH1",
|
||||||
|
"BOSSI0",
|
||||||
|
"BOSSJ0",
|
||||||
|
"BOSSK0",
|
||||||
|
"BOSSL0",
|
||||||
|
"BOSSO0"
|
||||||
|
],
|
||||||
|
"pistol": [
|
||||||
|
"PISGA0",
|
||||||
|
"PISGB0",
|
||||||
|
"PISGC0",
|
||||||
|
"PISGD0",
|
||||||
|
"PISGE0"
|
||||||
|
],
|
||||||
|
"shotgun": [
|
||||||
|
"SHTGA0",
|
||||||
|
"SHTGB0",
|
||||||
|
"SHTGC0",
|
||||||
|
"SHTGD0"
|
||||||
|
],
|
||||||
|
"fire": [
|
||||||
|
"BAL1A0",
|
||||||
|
"BAL1B0",
|
||||||
|
"BAL1C0",
|
||||||
|
"BAL1D0",
|
||||||
|
"BAL1E0"
|
||||||
|
],
|
||||||
|
"portal": [
|
||||||
|
"TFOGA0",
|
||||||
|
"TFOGB0",
|
||||||
|
"TFOGC0",
|
||||||
|
"TFOGD0",
|
||||||
|
"TFOGE0",
|
||||||
|
"TFOGF0",
|
||||||
|
"TFOGG0",
|
||||||
|
"TFOGH0"
|
||||||
|
],
|
||||||
|
"items": [
|
||||||
|
"MEDIA0",
|
||||||
|
"ARM1A0",
|
||||||
|
"CLIPA0",
|
||||||
|
"SHELA0",
|
||||||
|
"SHOTA0",
|
||||||
|
"BKEYA0",
|
||||||
|
"BON2A0"
|
||||||
|
],
|
||||||
|
"materials": [
|
||||||
|
"MARBLE1",
|
||||||
|
"GSTONE1",
|
||||||
|
"SKIN2",
|
||||||
|
"BIGDOOR2",
|
||||||
|
"SW1GARG",
|
||||||
|
"SP_HOT1",
|
||||||
|
"FLOOR7_2",
|
||||||
|
"CEIL5_2",
|
||||||
|
"LAVA1",
|
||||||
|
"LAVA2",
|
||||||
|
"LAVA3",
|
||||||
|
"LAVA4"
|
||||||
|
],
|
||||||
|
"hud": [
|
||||||
|
"STBAR"
|
||||||
|
],
|
||||||
|
"digits": [
|
||||||
|
"STTNUM0",
|
||||||
|
"STTNUM1",
|
||||||
|
"STTNUM2",
|
||||||
|
"STTNUM3",
|
||||||
|
"STTNUM4",
|
||||||
|
"STTNUM5",
|
||||||
|
"STTNUM6",
|
||||||
|
"STTNUM7",
|
||||||
|
"STTNUM8",
|
||||||
|
"STTNUM9",
|
||||||
|
"STTPRCNT"
|
||||||
|
],
|
||||||
|
"faces": [
|
||||||
|
"STFST00",
|
||||||
|
"STFST01",
|
||||||
|
"STFST02",
|
||||||
|
"STFOUCH0",
|
||||||
|
"STFST10",
|
||||||
|
"STFST11",
|
||||||
|
"STFST12",
|
||||||
|
"STFOUCH1",
|
||||||
|
"STFST20",
|
||||||
|
"STFST21",
|
||||||
|
"STFST22",
|
||||||
|
"STFOUCH2",
|
||||||
|
"STFST30",
|
||||||
|
"STFST31",
|
||||||
|
"STFST32",
|
||||||
|
"STFOUCH3",
|
||||||
|
"STFST40",
|
||||||
|
"STFST41",
|
||||||
|
"STFST42",
|
||||||
|
"STFOUCH4",
|
||||||
|
"STFDEAD0"
|
||||||
|
],
|
||||||
|
"impacts": [
|
||||||
|
"PUFFA0",
|
||||||
|
"PUFFB0",
|
||||||
|
"PUFFC0",
|
||||||
|
"PUFFD0"
|
||||||
|
],
|
||||||
|
"painting": [
|
||||||
|
"SKY3",
|
||||||
|
"LAVA1",
|
||||||
|
"GSTONE1",
|
||||||
|
"BOSSA1",
|
||||||
|
"TROOA1",
|
||||||
|
"SARGA1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
assets/hell/materials.bin
Normal file
BIN
assets/hell/materials.bin
Normal file
Binary file not shown.
BIN
assets/hell/painting-preview.png
Normal file
BIN
assets/hell/painting-preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/hell/painting.bin
Normal file
BIN
assets/hell/painting.bin
Normal file
Binary file not shown.
BIN
assets/hell/pistol.bin
Normal file
BIN
assets/hell/pistol.bin
Normal file
Binary file not shown.
BIN
assets/hell/portal.bin
Normal file
BIN
assets/hell/portal.bin
Normal file
Binary file not shown.
BIN
assets/hell/preview.png
Normal file
BIN
assets/hell/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
assets/hell/shotgun.bin
Normal file
BIN
assets/hell/shotgun.bin
Normal file
Binary file not shown.
BIN
assets/hell/soldier.bin
Normal file
BIN
assets/hell/soldier.bin
Normal file
Binary file not shown.
BIN
bin/poker.exe
BIN
bin/poker.exe
Binary file not shown.
|
|
@ -8,7 +8,7 @@ namespace AssemblerCasinoLauncher
|
||||||
{
|
{
|
||||||
static class Program
|
static class Program
|
||||||
{
|
{
|
||||||
const string ExpectedPokerSha256 = "B0F5CE31D9A2467CFE8AF66CA90CC545F75318F11D149473A4377A2A278C8B23";
|
const string ExpectedPokerSha256 = "EC24778AE95D57B80247180AB0121279C8224A2B7E36801A168AEEF391E6B34B";
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
static void Main()
|
static void Main()
|
||||||
|
|
|
||||||
Binary file not shown.
42
casino3d.inc
42
casino3d.inc
|
|
@ -175,6 +175,7 @@ include src_graphics\backrooms_data.inc
|
||||||
include src_graphics\observer_data.inc
|
include src_graphics\observer_data.inc
|
||||||
include src_graphics\darts_data.inc
|
include src_graphics\darts_data.inc
|
||||||
include src_graphics\jackbox_data.inc
|
include src_graphics\jackbox_data.inc
|
||||||
|
include src_graphics\hell_fps_data.inc
|
||||||
|
|
||||||
.data
|
.data
|
||||||
ALIGN 4
|
ALIGN 4
|
||||||
|
|
@ -472,7 +473,7 @@ screenBuffer dd SCREEN_SIZE dup(?)
|
||||||
ALIGN 4
|
ALIGN 4
|
||||||
zBuffer dd SCREEN_W dup(?)
|
zBuffer dd SCREEN_W dup(?)
|
||||||
ALIGN 4
|
ALIGN 4
|
||||||
texturesBuffer dd (185 * 128 * 128) dup(?)
|
texturesBuffer dd (197 * 128 * 128) dup(?)
|
||||||
|
|
||||||
hudNumBuffer db 32 dup(?)
|
hudNumBuffer db 32 dup(?)
|
||||||
|
|
||||||
|
|
@ -4496,6 +4497,8 @@ unlock_done:
|
||||||
UnlockMouse3D ENDP
|
UnlockMouse3D ENDP
|
||||||
|
|
||||||
UpdateMouseLockState PROC
|
UpdateMouseLockState PROC
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
je lock_interior_check
|
||||||
cmp engineMode,MODE_STAIRS
|
cmp engineMode,MODE_STAIRS
|
||||||
je lock_interior_check
|
je lock_interior_check
|
||||||
cmp engineMode, MODE_WALK
|
cmp engineMode, MODE_WALK
|
||||||
|
|
@ -4567,6 +4570,10 @@ skip_mouse_look:
|
||||||
mov eax,50
|
mov eax,50
|
||||||
@@:
|
@@:
|
||||||
imul eax, 200
|
imul eax, 200
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
jne @F
|
||||||
|
imul eax,2
|
||||||
|
@@:
|
||||||
mov speed, eax
|
mov speed, eax
|
||||||
cmp keys3D[10h], 0
|
cmp keys3D[10h], 0
|
||||||
je @F
|
je @F
|
||||||
|
|
@ -4667,19 +4674,33 @@ check_total_movement:
|
||||||
add eax, deltaMoveY
|
add eax, deltaMoveY
|
||||||
mov nextY, eax
|
mov nextY, eax
|
||||||
|
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
jne hf_move_regular_x
|
||||||
|
invoke HFPlayerCanWalk,nextX,playerY
|
||||||
|
jmp hf_move_check_x
|
||||||
|
hf_move_regular_x:
|
||||||
invoke CanWalk3D, nextX, playerY
|
invoke CanWalk3D, nextX, playerY
|
||||||
|
hf_move_check_x:
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz @F
|
jz @F
|
||||||
mov eax, nextX
|
mov eax, nextX
|
||||||
mov playerX, eax
|
mov playerX, eax
|
||||||
@@:
|
@@:
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
jne hf_move_regular_y
|
||||||
|
invoke HFPlayerCanWalk,playerX,nextY
|
||||||
|
jmp hf_move_check_y
|
||||||
|
hf_move_regular_y:
|
||||||
invoke CanWalk3D, playerX, nextY
|
invoke CanWalk3D, playerX, nextY
|
||||||
|
hf_move_check_y:
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz input_action
|
jz input_action
|
||||||
mov eax, nextY
|
mov eax, nextY
|
||||||
mov playerY, eax
|
mov playerY, eax
|
||||||
|
|
||||||
input_action:
|
input_action:
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
je player_input_complete
|
||||||
cmp brActive3D,0
|
cmp brActive3D,0
|
||||||
jne player_input_complete
|
jne player_input_complete
|
||||||
cmp engineMode,MODE_STAIRS
|
cmp engineMode,MODE_STAIRS
|
||||||
|
|
@ -5254,6 +5275,9 @@ include src_graphics\observer.inc
|
||||||
include src_graphics\darts.inc
|
include src_graphics\darts.inc
|
||||||
include src_graphics\jackbox.inc
|
include src_graphics\jackbox.inc
|
||||||
include src_graphics\backrooms.inc
|
include src_graphics\backrooms.inc
|
||||||
|
include src_graphics\hell_fps.inc
|
||||||
|
include src_graphics\hell_fps_combat.inc
|
||||||
|
include src_graphics\hell_fps_render.inc
|
||||||
|
|
||||||
; --- Main Loop of the 3D Casino Engine ---
|
; --- Main Loop of the 3D Casino Engine ---
|
||||||
StartHellTower3D PROC uses ebx esi edi
|
StartHellTower3D PROC uses ebx esi edi
|
||||||
|
|
@ -5317,6 +5341,11 @@ engine_observer_skip:
|
||||||
invoke Pressed3D, 1Bh
|
invoke Pressed3D, 1Bh
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz engine_tick
|
jz engine_tick
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
jne @F
|
||||||
|
call LeaveHellFPS3D
|
||||||
|
jmp engine_tick
|
||||||
|
@@:
|
||||||
cmp engineMode, MODE_INTRO
|
cmp engineMode, MODE_INTRO
|
||||||
jne @F
|
jne @F
|
||||||
call FinishStartup3D
|
call FinishStartup3D
|
||||||
|
|
@ -5397,6 +5426,14 @@ check_arrival_mode:
|
||||||
call RenderArrival3D
|
call RenderArrival3D
|
||||||
jmp present_frame
|
jmp present_frame
|
||||||
check_walk_mode:
|
check_walk_mode:
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
jne @F
|
||||||
|
call HandleHellFPS3D
|
||||||
|
cmp engineMode,MODE_WALK
|
||||||
|
je check_walk_mode
|
||||||
|
call RenderHellFPS3D
|
||||||
|
jmp present_frame
|
||||||
|
@@:
|
||||||
cmp engineMode, MODE_WALK
|
cmp engineMode, MODE_WALK
|
||||||
jne check_slots_mode
|
jne check_slots_mode
|
||||||
cmp brActive3D,0
|
cmp brActive3D,0
|
||||||
|
|
@ -5405,6 +5442,8 @@ check_walk_mode:
|
||||||
call UpdateCamera3D
|
call UpdateCamera3D
|
||||||
call CheckInteractions
|
call CheckInteractions
|
||||||
call HandlePlayerInput
|
call HandlePlayerInput
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
je check_walk_mode
|
||||||
call BackroomsEntry3D
|
call BackroomsEntry3D
|
||||||
cmp brActive3D,0
|
cmp brActive3D,0
|
||||||
jne engine_backrooms
|
jne engine_backrooms
|
||||||
|
|
@ -5741,6 +5780,7 @@ engine_idle:
|
||||||
invoke Sleep, 50
|
invoke Sleep, 50
|
||||||
jmp engine_loop
|
jmp engine_loop
|
||||||
engine_shutdown:
|
engine_shutdown:
|
||||||
|
call FreeHellFPS3D
|
||||||
call StopFortuneWorker3D
|
call StopFortuneWorker3D
|
||||||
call SettleScratchOnQuit3D
|
call SettleScratchOnQuit3D
|
||||||
call UnlockMouse3D
|
call UnlockMouse3D
|
||||||
|
|
|
||||||
BIN
dist/Assembler Casino.exe
vendored
BIN
dist/Assembler Casino.exe
vendored
Binary file not shown.
25
docs/HELL_ASSET_PIPELINE.md
Normal file
25
docs/HELL_ASSET_PIPELINE.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Hell presentation asset contract
|
||||||
|
|
||||||
|
Imported offline from the pinned Freedoom 0.13.0 archive using
|
||||||
|
`tools/generators/import_freedoom.py`. Runtime remains MASM32.
|
||||||
|
|
||||||
|
All packs: 20-byte `ANM1` header (five little-endian DWORDs: magic, width, height,
|
||||||
|
frame count, payload bytes), then row-major BGRA frames. Nearest-neighbour only.
|
||||||
|
|
||||||
|
| Pack | Frame size | Layout / alpha / origin |
|
||||||
|
|---|---|---|
|
||||||
|
| `hud.bin` | 320×32 | One `STBAR` frame, rendered 2× at (0,416) |
|
||||||
|
| `digits.bin` | 16×16 | Ten digits and percent, right-aligned; transparent |
|
||||||
|
| `faces.bin` | 24×32 | Five health bands × four expressions, then dead; bottom-centred |
|
||||||
|
| `impacts.bin` | 32×32 | Four `PUFF` frames, shared sprite origin; transparent |
|
||||||
|
| `painting.bin` | 512×256 | One opaque, power-of-two wall texture; full-frame UVs |
|
||||||
|
|
||||||
|
Painting composes Freedoom sky, lava, masonry and monsters with an authored gilt
|
||||||
|
border. Its preview uses intended landscape aspect. Engine maps the texture onto
|
||||||
|
a 6×2-world-unit wall panel; it is not a camera-facing sprite. Power-of-two
|
||||||
|
dimensions are required by `DrawCityVertical3D`'s masked texture coordinates.
|
||||||
|
No generated AI image or proprietary Doom artwork is used.
|
||||||
|
|
||||||
|
HUD glyphs and portrait retain original pixel-art palette. Frame padding avoids
|
||||||
|
per-expression scaling jitter. See `assets/hell/manifest.json` for source lumps
|
||||||
|
and `assets/hell/COPYING.txt` / `CREDITS.txt` for licence and attribution.
|
||||||
94
docs/HELL_EXPEDITION.md
Normal file
94
docs/HELL_EXPEDITION.md
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Hell Expedition
|
||||||
|
|
||||||
|
## Play
|
||||||
|
|
||||||
|
Launch `poker.exe` or `dist/Assembler Casino.exe`. On the casino's first floor,
|
||||||
|
approach the large gilt-framed hell painting on the wall opposite the bar
|
||||||
|
(map position 6, 30.9), face it and press **E**. The upstairs Hell Tower remains
|
||||||
|
a separate game. The old freestanding portal and its collision frame are gone.
|
||||||
|
|
||||||
|
| Input | Action |
|
||||||
|
|---|---|
|
||||||
|
| WASD | Move / strafe |
|
||||||
|
| Mouse | Turn |
|
||||||
|
| Left mouse / Space | Fire; hold for repeated fire |
|
||||||
|
| 1 / 2 | Pistol / shotgun, after collecting it |
|
||||||
|
| E | Open nearby gates/secrets; activate final exit |
|
||||||
|
| Enter after death | Restart expedition |
|
||||||
|
| Esc | Return to casino |
|
||||||
|
|
||||||
|
Collect the shotgun near the starting point. Cross the barracks and lava court,
|
||||||
|
find the blue key in the northern crypt, open the eastern gate and defeat the
|
||||||
|
warden in the southern sanctum. Use the exit portal to finish. Two concealed
|
||||||
|
side rooms contain extra supplies. Lava causes damage; armour absorbs half of
|
||||||
|
incoming damage while it lasts. Health and ammunition are expedition-local.
|
||||||
|
|
||||||
|
Death and retry do not deduct casino money. The first successful clear credits
|
||||||
|
**$6,660**, subject to the existing bank cap. Later clears show statistics without
|
||||||
|
paying again. Leaving or retrying starts a fresh expedition; mid-level progress
|
||||||
|
is not saved. Completion belongs to the current casino profile.
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
- MASM32 runtime: `src_graphics/hell_fps*.inc`; integrated with the existing
|
||||||
|
raycaster, collision, window/input and signed profile code.
|
||||||
|
- Authored 32×32 map: five encounter regions, 28 enemies across four types,
|
||||||
|
24 pickups, a blue-key gate, two secrets and a boss-controlled exit.
|
||||||
|
- Combat simulation: fixed 35 Hz; existing frame-time-scaled player movement.
|
||||||
|
- Hitscan pistol/shotgun, wall occlusion, enemy attack windups, BFS navigation,
|
||||||
|
projectile pool, health/armour, weapon and monster animation, HUD/statistics.
|
||||||
|
- Shotgun: seven independent pellets per shell, approximately 12.7-degree
|
||||||
|
horizontal cone, individual damage/occlusion and short-lived impact puffs.
|
||||||
|
A single shot can hit several enemies; the pistol remains a single ray.
|
||||||
|
- Doom-layout Freedoom status bar: large red ammunition/health/armour numbers,
|
||||||
|
damage-dependent animated face, weapon slots, key and ammunition reserves.
|
||||||
|
- Completion flag: bit 18 in native and preview signed saves; file sizes stay
|
||||||
|
unchanged. Native profiles remain the normal game save path.
|
||||||
|
- Music: existing `assets/music/tower/Mick_Gordon_-_The_Only_Thing_They_Fear_Is_You_OST_DOOM_Eternal_72020713.mp3`.
|
||||||
|
Dedicated MCI music alias at 35% of the previous music level (still follows
|
||||||
|
the volume setting, including mute), applied before playback starts.
|
||||||
|
Six independent waveaudio effect channels retain their existing loudness.
|
||||||
|
|
||||||
|
## Assets and regeneration
|
||||||
|
|
||||||
|
Graphics and effects originate from **Freedoom 0.13.0**, imported offline into
|
||||||
|
ANM1 BGRA packs and WAV files. Licence and credits ship in
|
||||||
|
`assets/hell/COPYING.txt` and `assets/hell/CREDITS.txt`. Exact source lumps,
|
||||||
|
download URL and archive SHA-256 are in `assets/hell/manifest.json`.
|
||||||
|
The casino level is original; no Doom WAD is loaded at runtime.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python tools/generators/import_freedoom.py path/to/freedoom-0.13.0.zip
|
||||||
|
python tools/generators/make_hell_level.py
|
||||||
|
cmd /c build.bat
|
||||||
|
powershell -ExecutionPolicy Bypass -File tools/tests/publish_graphics.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The final command updates local root/bin game copies and recompiles the existing
|
||||||
|
C# launcher with the new integrity hash. It does not upload a release.
|
||||||
|
Python/Pillow are offline development dependencies only. Include all of
|
||||||
|
`assets/hell/` and the existing soundtrack when copying the game distribution.
|
||||||
|
Freedoom's licence applies to its assets, not to the separate soundtrack.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python tools/tests/check_hell_fps.py
|
||||||
|
python tools/tests/check_hell_fps.py --window
|
||||||
|
python tools/tests/check_artifacts.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Native checks execute shipping ASM procedures: portal targeting, wall/body
|
||||||
|
collisions, single-ray pistol versus multi-target pellet spread, wall occlusion,
|
||||||
|
one-shell consumption/empty weapon, impact pool, pickups, armour/death/retry, key gate,
|
||||||
|
secrets, 1,050 AI ticks, a finite-ammo boss fight with strafing and normal health,
|
||||||
|
boss-locked exit, one-time reward, both signed save
|
||||||
|
formats and return to the hall. Framebuffers are saved in `build/scratch/`.
|
||||||
|
The window check presents 120 frames through Win32, sends mouse press/release
|
||||||
|
events, checks shots, all six SFX opens and music's MCI playing status, then
|
||||||
|
returns and closes. It also exercises asset-path resolution from a non-root
|
||||||
|
working directory. Tests compare all `runtime/` file hashes before/after.
|
||||||
|
|
||||||
|
These are automated mechanics/render/audio checks, not a complete human
|
||||||
|
playthrough. The requested 10–15-minute first-clear duration and final difficulty
|
||||||
|
still need playtesting; no measured duration is claimed.
|
||||||
19
poker.asm
19
poker.asm
|
|
@ -827,6 +827,10 @@ save_stats_played_loop:
|
||||||
shl ecx,17
|
shl ecx,17
|
||||||
or eax,ecx
|
or eax,ecx
|
||||||
or eax,2
|
or eax,2
|
||||||
|
mov ecx,hfCompleted
|
||||||
|
and ecx,1
|
||||||
|
shl ecx,18
|
||||||
|
or eax,ecx
|
||||||
mov DWORD PTR [saveBuffer+304],eax
|
mov DWORD PTR [saveBuffer+304],eax
|
||||||
invoke ArtifactSaveSignature3D,OFFSET saveBuffer
|
invoke ArtifactSaveSignature3D,OFFSET saveBuffer
|
||||||
mov DWORD PTR [saveBuffer+308],eax
|
mov DWORD PTR [saveBuffer+308],eax
|
||||||
|
|
@ -902,6 +906,10 @@ load_file_opened:
|
||||||
shr eax,17
|
shr eax,17
|
||||||
and eax,1
|
and eax,1
|
||||||
mov brEnabled3D,eax
|
mov brEnabled3D,eax
|
||||||
|
mov eax,DWORD PTR [saveBuffer+304]
|
||||||
|
shr eax,18
|
||||||
|
and eax,1
|
||||||
|
mov hfCompleted,eax
|
||||||
load_signature_ok:
|
load_signature_ok:
|
||||||
mov eax, DWORD PTR [saveBuffer+4]
|
mov eax, DWORD PTR [saveBuffer+4]
|
||||||
mov bankroll, eax
|
mov bankroll, eax
|
||||||
|
|
@ -2061,7 +2069,18 @@ ENDIF
|
||||||
IFDEF BACKROOMS_TEST
|
IFDEF BACKROOMS_TEST
|
||||||
include tools/tests/backrooms_test.inc
|
include tools/tests/backrooms_test.inc
|
||||||
ENDIF
|
ENDIF
|
||||||
|
IFDEF HELL_FPS_TEST
|
||||||
|
include tools/tests/hell_fps_test.inc
|
||||||
|
ENDIF
|
||||||
main PROC
|
main PROC
|
||||||
|
IFDEF HELL_FPS_TEST
|
||||||
|
IFDEF HELL_FPS_WINDOW_TEST
|
||||||
|
call RunHellFPSWindowTest
|
||||||
|
ELSE
|
||||||
|
call RunHellFPSTest
|
||||||
|
ENDIF
|
||||||
|
invoke ExitProcess,eax
|
||||||
|
ENDIF
|
||||||
IFDEF BACKROOMS_TEST
|
IFDEF BACKROOMS_TEST
|
||||||
call RunBackroomsTest3D
|
call RunBackroomsTest3D
|
||||||
invoke ExitProcess,eax
|
invoke ExitProcess,eax
|
||||||
|
|
|
||||||
BIN
poker.exe
BIN
poker.exe
Binary file not shown.
BIN
poker.obj
BIN
poker.obj
Binary file not shown.
|
|
@ -33,7 +33,7 @@ ValidateArtifactSave3D PROC uses esi edi source:DWORD
|
||||||
cmp DWORD PTR [esi+280],32465241h
|
cmp DWORD PTR [esi+280],32465241h
|
||||||
jne artifact_save_invalid
|
jne artifact_save_invalid
|
||||||
mov eax,[esi+304]
|
mov eax,[esi+304]
|
||||||
and eax,0FFFCFFFFh
|
and eax,0FFF8FFFFh
|
||||||
cmp eax,2
|
cmp eax,2
|
||||||
jne artifact_save_invalid
|
jne artifact_save_invalid
|
||||||
test DWORD PTR [esi+304],20000h
|
test DWORD PTR [esi+304],20000h
|
||||||
|
|
@ -72,6 +72,7 @@ ArtifactCredit3D PROC amount:DWORD
|
||||||
ArtifactCredit3D ENDP
|
ArtifactCredit3D ENDP
|
||||||
|
|
||||||
ResetArtifacts3D PROC
|
ResetArtifacts3D PROC
|
||||||
|
mov hfCompleted,0
|
||||||
mov brOwned3D,0
|
mov brOwned3D,0
|
||||||
mov brEnabled3D,1
|
mov brEnabled3D,1
|
||||||
mov charmCloverUses,0
|
mov charmCloverUses,0
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,23 @@ cityRayY3D dd SCREEN_W dup(?)
|
||||||
; Exterior-only materials do not consume or overwrite the hall's texture slots.
|
; Exterior-only materials do not consume or overwrite the hall's texture slots.
|
||||||
ResolveCitySurface3D PROC material:DWORD
|
ResolveCitySurface3D PROC material:DWORD
|
||||||
mov eax,material
|
mov eax,material
|
||||||
|
cmp eax,HF_PAINTING_MATERIAL
|
||||||
|
jne hf_not_painting
|
||||||
|
mov eax,hfPaintingArt.pixels
|
||||||
|
mov ecx,512
|
||||||
|
mov edx,256
|
||||||
|
ret
|
||||||
|
hf_not_painting:
|
||||||
|
cmp eax,HF_WALL
|
||||||
|
jb hf_surface_other
|
||||||
|
cmp eax,HF_LAVA+3
|
||||||
|
ja hf_surface_other
|
||||||
|
shl eax,16
|
||||||
|
add eax,OFFSET texturesBuffer
|
||||||
|
mov ecx,128
|
||||||
|
mov edx,128
|
||||||
|
ret
|
||||||
|
hf_surface_other:
|
||||||
cmp hallLevel3D,HALL_BACKROOMS
|
cmp hallLevel3D,HALL_BACKROOMS
|
||||||
jne br_surface_other_world
|
jne br_surface_other_world
|
||||||
cmp eax,BR_WALL
|
cmp eax,BR_WALL
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ hall_plane_row:
|
||||||
mov eax, viewCeiling3D
|
mov eax, viewCeiling3D
|
||||||
sub eax, viewEye3D
|
sub eax, viewEye3D
|
||||||
mov material, 101
|
mov material, 101
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
jne @F
|
||||||
|
mov material,HF_CEILING
|
||||||
|
@@:
|
||||||
cmp brActive3D,0
|
cmp brActive3D,0
|
||||||
je @F
|
je @F
|
||||||
cmp hallLevel3D,HALL_BACKROOMS
|
cmp hallLevel3D,HALL_BACKROOMS
|
||||||
|
|
@ -756,6 +760,7 @@ hall_render_lounge:
|
||||||
invoke DrawCityObject3D,OFFSET scrStation3D,0
|
invoke DrawCityObject3D,OFFSET scrStation3D,0
|
||||||
call RenderHallDressing3D
|
call RenderHallDressing3D
|
||||||
call RenderHallPortals3D
|
call RenderHallPortals3D
|
||||||
|
call RenderHellPortal3D
|
||||||
call RenderFortuneEntrance3D
|
call RenderFortuneEntrance3D
|
||||||
call RenderLift3D
|
call RenderLift3D
|
||||||
call ApplyHallLighting3D
|
call ApplyHallLighting3D
|
||||||
|
|
@ -865,6 +870,7 @@ hall_check_other:
|
||||||
call CheckNpcInteractions3D
|
call CheckNpcInteractions3D
|
||||||
call CheckFortuneInteractions3D
|
call CheckFortuneInteractions3D
|
||||||
call CheckRoofInteractions3D
|
call CheckRoofInteractions3D
|
||||||
|
invoke HallTarget3D,HF_PORTAL_X,HF_PORTAL_Y,INTERACT_HELL_FPS,OFFSET hfPortalPrompt
|
||||||
ret
|
ret
|
||||||
hall_check_bunker:
|
hall_check_bunker:
|
||||||
mov interactTargetId, 0
|
mov interactTargetId, 0
|
||||||
|
|
@ -874,6 +880,12 @@ hall_check_bunker:
|
||||||
CheckHallInteractions3D ENDP
|
CheckHallInteractions3D ENDP
|
||||||
|
|
||||||
TriggerHallInteraction3D PROC
|
TriggerHallInteraction3D PROC
|
||||||
|
cmp interactTargetId,INTERACT_HELL_FPS
|
||||||
|
jne @F
|
||||||
|
call BeginHellFPS3D
|
||||||
|
mov eax,1
|
||||||
|
ret
|
||||||
|
@@:
|
||||||
call TryBowlingInteraction3D
|
call TryBowlingInteraction3D
|
||||||
test eax,eax
|
test eax,eax
|
||||||
jnz hall_trigger_handled
|
jnz hall_trigger_handled
|
||||||
|
|
|
||||||
277
src_graphics/hell_fps.inc
Normal file
277
src_graphics/hell_fps.inc
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
; Expedition lifecycle and fixed 35 Hz simulation. No external game runtime.
|
||||||
|
HFPlaySound PROC channel:DWORD
|
||||||
|
cmp hfSoundReady,0
|
||||||
|
je @F
|
||||||
|
mov eax,channel
|
||||||
|
cmp eax,6
|
||||||
|
jae @F
|
||||||
|
invoke mciSendStringA,hfSndPlays[eax*4],0,0,0
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
HFPlaySound ENDP
|
||||||
|
|
||||||
|
HFLoad PROC uses esi edi
|
||||||
|
cmp hfLoaded,1
|
||||||
|
je hf_load_ok
|
||||||
|
xor esi,esi
|
||||||
|
hf_load_next:
|
||||||
|
invoke LoadAnimPack3D,hfPacks[esi*4],hfPaths[esi*4]
|
||||||
|
test eax,eax
|
||||||
|
jz hf_load_failed
|
||||||
|
inc esi
|
||||||
|
cmp esi,HF_PACK_COUNT
|
||||||
|
jb hf_load_next
|
||||||
|
; Dedicated texture slots, never overwrite casino materials.
|
||||||
|
mov esi,hfMaterials.pixels
|
||||||
|
mov edi,OFFSET texturesBuffer+HF_WALL*65536
|
||||||
|
mov ecx,12*128*128
|
||||||
|
cld
|
||||||
|
rep movsd
|
||||||
|
mov hfLoaded,1
|
||||||
|
hf_load_ok:
|
||||||
|
mov eax,1
|
||||||
|
ret
|
||||||
|
hf_load_failed:
|
||||||
|
call FreeHellFPS3D
|
||||||
|
xor eax,eax
|
||||||
|
ret
|
||||||
|
HFLoad ENDP
|
||||||
|
|
||||||
|
FreeHellFPS3D PROC uses esi
|
||||||
|
xor esi,esi
|
||||||
|
@@:
|
||||||
|
invoke FreeAnimPack3D,hfPacks[esi*4]
|
||||||
|
inc esi
|
||||||
|
cmp esi,HF_PACK_COUNT
|
||||||
|
jb @B
|
||||||
|
mov hfLoaded,0
|
||||||
|
invoke mciSendStringA,OFFSET hfMusicClose,0,0,0
|
||||||
|
xor esi,esi
|
||||||
|
@@:
|
||||||
|
invoke mciSendStringA,hfSndCloses[esi*4],0,0,0
|
||||||
|
inc esi
|
||||||
|
cmp esi,6
|
||||||
|
jb @B
|
||||||
|
mov hfSoundReady,0
|
||||||
|
ret
|
||||||
|
FreeHellFPS3D ENDP
|
||||||
|
|
||||||
|
HFReset PROC uses esi edi ebx
|
||||||
|
mov esi,OFFSET hfMapTemplate
|
||||||
|
mov edi,OFFSET hfMap
|
||||||
|
mov ecx,256
|
||||||
|
cld
|
||||||
|
rep movsd
|
||||||
|
xor eax,eax
|
||||||
|
mov edi,OFFSET hfBolts
|
||||||
|
mov ecx,SIZEOF hfBolts/4
|
||||||
|
rep stosd
|
||||||
|
mov edi,OFFSET hfImpacts
|
||||||
|
mov ecx,SIZEOF hfImpacts/4
|
||||||
|
rep stosd
|
||||||
|
mov edi,OFFSET hfItemTaken
|
||||||
|
mov ecx,HF_ITEMS
|
||||||
|
rep stosd
|
||||||
|
mov esi,OFFSET hfEnemySeeds
|
||||||
|
mov edi,OFFSET hfEnemies
|
||||||
|
mov ebx,HF_ENEMIES
|
||||||
|
hf_reset_enemy:
|
||||||
|
lodsd
|
||||||
|
mov [edi].HFEnemy.x,eax
|
||||||
|
lodsd
|
||||||
|
mov [edi].HFEnemy.y,eax
|
||||||
|
lodsd
|
||||||
|
mov [edi].HFEnemy.kind,eax
|
||||||
|
mov eax,hfMonsterHP[eax*4]
|
||||||
|
mov [edi].HFEnemy.hp,eax
|
||||||
|
mov [edi].HFEnemy.cooldown,35
|
||||||
|
mov [edi].HFEnemy.pose,0
|
||||||
|
mov [edi].HFEnemy.timer,0
|
||||||
|
mov [edi].HFEnemy.awake,0
|
||||||
|
add edi,SIZEOF HFEnemy
|
||||||
|
dec ebx
|
||||||
|
jnz hf_reset_enemy
|
||||||
|
mov hfState,0
|
||||||
|
mov hfHealth,100
|
||||||
|
mov hfArmor,0
|
||||||
|
mov hfBullets,80
|
||||||
|
mov hfShells,0
|
||||||
|
mov hfWeapon,0
|
||||||
|
mov hfHasShotgun,0
|
||||||
|
mov hfKey,0
|
||||||
|
mov hfSecrets,0
|
||||||
|
mov hfKills,0
|
||||||
|
mov hfTicks,0
|
||||||
|
mov hfAccumulator,0
|
||||||
|
mov hfCooldown,0
|
||||||
|
mov hfFlash,0
|
||||||
|
mov hfHurt,0
|
||||||
|
mov hfDoorMask,0
|
||||||
|
mov hfAwarded,0
|
||||||
|
mov hfFireHeld,0
|
||||||
|
mov hfFirePending,0
|
||||||
|
mov hfUsePending,0
|
||||||
|
mov playerX,3*FP_1+FP_HALF
|
||||||
|
mov playerY,26*FP_1+FP_HALF
|
||||||
|
mov playerAngle,0
|
||||||
|
mov worldMap3D,OFFSET hfMap
|
||||||
|
mov hallLevel3D,99
|
||||||
|
mov floorTexture3D,HF_FLOOR
|
||||||
|
mov viewEye3D,FP_1
|
||||||
|
mov viewCeiling3D,FP_1*5/2
|
||||||
|
mov outdoorRender3D,0
|
||||||
|
mov viewHorizon3D,HALF_H
|
||||||
|
mov engineMode,MODE_HELL_FPS
|
||||||
|
mov hfMessage,OFFSET hfWelcomeText
|
||||||
|
mov hfMessageTime,210
|
||||||
|
call ClearKeys3D
|
||||||
|
call UpdateCamera3D
|
||||||
|
call HFBuildFlow
|
||||||
|
ret
|
||||||
|
HFReset ENDP
|
||||||
|
|
||||||
|
BeginHellFPS3D PROC uses esi
|
||||||
|
call HFLoad
|
||||||
|
test eax,eax
|
||||||
|
jnz @F
|
||||||
|
invoke CopyString3D,OFFSET hfMissingText,OFFSET flashMessage
|
||||||
|
mov flashTimer,180
|
||||||
|
ret
|
||||||
|
@@:
|
||||||
|
mov eax,playerX
|
||||||
|
mov hfOldX,eax
|
||||||
|
mov eax,playerY
|
||||||
|
mov hfOldY,eax
|
||||||
|
mov eax,playerAngle
|
||||||
|
mov hfOldAngle,eax
|
||||||
|
mov eax,worldMap3D
|
||||||
|
mov hfOldMap,eax
|
||||||
|
mov eax,floorTexture3D
|
||||||
|
mov hfOldFloor,eax
|
||||||
|
mov eax,hallLevel3D
|
||||||
|
mov hfOldLevel,eax
|
||||||
|
mov eax,viewEye3D
|
||||||
|
mov hfOldEye,eax
|
||||||
|
mov eax,viewCeiling3D
|
||||||
|
mov hfOldCeiling,eax
|
||||||
|
call StopTowerMusic
|
||||||
|
call StopJukeboxTrack3D
|
||||||
|
invoke PlayCasinoSound3D,0,0,0
|
||||||
|
invoke mciSendStringA,OFFSET hfMusicOpen,0,0,0
|
||||||
|
mov hfMusicError,eax
|
||||||
|
mov hfLastVolume,-1
|
||||||
|
call HFUpdateMusicVolume
|
||||||
|
invoke mciSendStringA,OFFSET hfMusicPlay,0,0,0
|
||||||
|
or hfMusicError,eax
|
||||||
|
mov hfSoundFailures,0
|
||||||
|
xor esi,esi
|
||||||
|
hf_open_sound:
|
||||||
|
invoke mciSendStringA,hfSndOpens[esi*4],0,0,0
|
||||||
|
test eax,eax
|
||||||
|
jz @F
|
||||||
|
inc hfSoundFailures
|
||||||
|
@@:
|
||||||
|
inc esi
|
||||||
|
cmp esi,6
|
||||||
|
jb hf_open_sound
|
||||||
|
mov hfSoundReady,1
|
||||||
|
call HFReset
|
||||||
|
ret
|
||||||
|
BeginHellFPS3D ENDP
|
||||||
|
|
||||||
|
LeaveHellFPS3D PROC uses esi
|
||||||
|
mov eax,hfOldX
|
||||||
|
mov playerX,eax
|
||||||
|
mov eax,hfOldY
|
||||||
|
mov playerY,eax
|
||||||
|
mov eax,hfOldAngle
|
||||||
|
mov playerAngle,eax
|
||||||
|
mov eax,hfOldMap
|
||||||
|
mov worldMap3D,eax
|
||||||
|
mov eax,hfOldFloor
|
||||||
|
mov floorTexture3D,eax
|
||||||
|
mov eax,hfOldLevel
|
||||||
|
mov hallLevel3D,eax
|
||||||
|
mov eax,hfOldEye
|
||||||
|
mov viewEye3D,eax
|
||||||
|
mov eax,hfOldCeiling
|
||||||
|
mov viewCeiling3D,eax
|
||||||
|
mov hfFireHeld,0
|
||||||
|
mov hfFirePending,0
|
||||||
|
mov engineMode,MODE_WALK
|
||||||
|
mov paused3D,0
|
||||||
|
invoke mciSendStringA,OFFSET hfMusicClose,0,0,0
|
||||||
|
xor esi,esi
|
||||||
|
@@:
|
||||||
|
invoke mciSendStringA,hfSndCloses[esi*4],0,0,0
|
||||||
|
inc esi
|
||||||
|
cmp esi,6
|
||||||
|
jb @B
|
||||||
|
mov hfSoundReady,0
|
||||||
|
call ClearKeys3D
|
||||||
|
call UpdateCamera3D
|
||||||
|
ret
|
||||||
|
LeaveHellFPS3D ENDP
|
||||||
|
|
||||||
|
HFUpdateMusicVolume PROC
|
||||||
|
mov eax,settingsVolume
|
||||||
|
cmp eax,hfLastVolume
|
||||||
|
je @F
|
||||||
|
mov hfLastVolume,eax
|
||||||
|
imul eax,HF_MUSIC_SCALE
|
||||||
|
invoke FormatNumber,eax,OFFSET hfNumber
|
||||||
|
invoke CopyCString,OFFSET hfVolumeCommand,OFFSET hfVolumePrefix
|
||||||
|
invoke AppendCString,OFFSET hfVolumeCommand,OFFSET hfNumber
|
||||||
|
invoke mciSendStringA,OFFSET hfVolumeCommand,0,0,0
|
||||||
|
mov hfVolumeError,eax
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
HFUpdateMusicVolume ENDP
|
||||||
|
|
||||||
|
HandleHellFPS3D PROC uses ebx
|
||||||
|
call HFUpdateMusicVolume
|
||||||
|
cmp hfState,0
|
||||||
|
je hf_handle_play
|
||||||
|
invoke Pressed3D,0Dh
|
||||||
|
test eax,eax
|
||||||
|
jz hf_handle_done
|
||||||
|
cmp hfState,1
|
||||||
|
jne hf_handle_exit
|
||||||
|
call HFReset
|
||||||
|
ret
|
||||||
|
hf_handle_exit:
|
||||||
|
call LeaveHellFPS3D
|
||||||
|
ret
|
||||||
|
hf_handle_play:
|
||||||
|
call HandlePlayerInput
|
||||||
|
invoke Pressed3D,31h
|
||||||
|
test eax,eax
|
||||||
|
jz @F
|
||||||
|
mov hfWeapon,0
|
||||||
|
@@:
|
||||||
|
invoke Pressed3D,32h
|
||||||
|
test eax,eax
|
||||||
|
jz @F
|
||||||
|
cmp hfHasShotgun,0
|
||||||
|
je @F
|
||||||
|
mov hfWeapon,1
|
||||||
|
@@:
|
||||||
|
invoke Pressed3D,45h
|
||||||
|
or hfUsePending,eax
|
||||||
|
mov eax,mouseClick3D
|
||||||
|
or hfFirePending,eax
|
||||||
|
mov mouseClick3D,0
|
||||||
|
mov eax,deltaTime3D
|
||||||
|
imul eax,35
|
||||||
|
add hfAccumulator,eax
|
||||||
|
hf_handle_tick:
|
||||||
|
cmp hfAccumulator,1000
|
||||||
|
jb hf_handle_done
|
||||||
|
sub hfAccumulator,1000
|
||||||
|
call HFTick
|
||||||
|
cmp hfState,0
|
||||||
|
je hf_handle_tick
|
||||||
|
mov hfAccumulator,0
|
||||||
|
hf_handle_done:
|
||||||
|
ret
|
||||||
|
HandleHellFPS3D ENDP
|
||||||
764
src_graphics/hell_fps_combat.inc
Normal file
764
src_graphics/hell_fps_combat.inc
Normal file
|
|
@ -0,0 +1,764 @@
|
||||||
|
; All positions/velocities are signed 16.16; bounded pools and fixed ticks.
|
||||||
|
HFDistance PROC x0:DWORD,y0:DWORD,x1:DWORD,y1:DWORD
|
||||||
|
mov eax,x0
|
||||||
|
sub eax,x1
|
||||||
|
cdq
|
||||||
|
xor eax,edx
|
||||||
|
sub eax,edx
|
||||||
|
mov ecx,eax
|
||||||
|
mov eax,y0
|
||||||
|
sub eax,y1
|
||||||
|
cdq
|
||||||
|
xor eax,edx
|
||||||
|
sub eax,edx
|
||||||
|
cmp eax,ecx
|
||||||
|
jae @F
|
||||||
|
mov eax,ecx
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
HFDistance ENDP
|
||||||
|
|
||||||
|
HFPlayerCanWalk PROC uses esi edi px:DWORD,py:DWORD
|
||||||
|
invoke CanWalk3D,px,py
|
||||||
|
test eax,eax
|
||||||
|
jz hf_player_walk_done
|
||||||
|
mov esi,OFFSET hfEnemies
|
||||||
|
mov edi,HF_ENEMIES
|
||||||
|
hf_player_body:
|
||||||
|
cmp [esi].HFEnemy.hp,0
|
||||||
|
jle hf_player_body_next
|
||||||
|
invoke HFDistance,px,py,[esi].HFEnemy.x,[esi].HFEnemy.y
|
||||||
|
cmp eax,FP_1*9/20
|
||||||
|
jb hf_player_body_block
|
||||||
|
hf_player_body_next:
|
||||||
|
add esi,SIZEOF HFEnemy
|
||||||
|
dec edi
|
||||||
|
jnz hf_player_body
|
||||||
|
mov eax,1
|
||||||
|
ret
|
||||||
|
hf_player_body_block:
|
||||||
|
xor eax,eax
|
||||||
|
hf_player_walk_done:
|
||||||
|
ret
|
||||||
|
HFPlayerCanWalk ENDP
|
||||||
|
|
||||||
|
HFLineClear PROC uses ebx esi edi x0:DWORD,y0:DWORD,x1:DWORD,y1:DWORD
|
||||||
|
local stepX:DWORD,stepY:DWORD,rx:DWORD,ry:DWORD
|
||||||
|
invoke HFDistance,x0,y0,x1,y1
|
||||||
|
shr eax,12 ; quarter of a collision radius per sample, <= 1/16 tile
|
||||||
|
inc eax
|
||||||
|
mov ebx,eax
|
||||||
|
mov eax,x1
|
||||||
|
sub eax,x0
|
||||||
|
cdq
|
||||||
|
idiv ebx
|
||||||
|
mov stepX,eax
|
||||||
|
mov eax,y1
|
||||||
|
sub eax,y0
|
||||||
|
cdq
|
||||||
|
idiv ebx
|
||||||
|
mov stepY,eax
|
||||||
|
mov esi,x0
|
||||||
|
mov edi,y0
|
||||||
|
hf_los_step:
|
||||||
|
add esi,stepX
|
||||||
|
add edi,stepY
|
||||||
|
mov eax,esi
|
||||||
|
sar eax,16
|
||||||
|
cmp eax,32
|
||||||
|
jae hf_los_blocked
|
||||||
|
mov ecx,edi
|
||||||
|
sar ecx,16
|
||||||
|
cmp ecx,32
|
||||||
|
jae hf_los_blocked
|
||||||
|
shl ecx,5
|
||||||
|
add eax,ecx
|
||||||
|
cmp hfMap[eax],0
|
||||||
|
jne hf_los_blocked
|
||||||
|
dec ebx
|
||||||
|
jnz hf_los_step
|
||||||
|
mov eax,1
|
||||||
|
ret
|
||||||
|
hf_los_blocked:
|
||||||
|
xor eax,eax
|
||||||
|
ret
|
||||||
|
HFLineClear ENDP
|
||||||
|
|
||||||
|
HFDamagePlayer PROC uses ebx amount:DWORD
|
||||||
|
cmp hfState,0
|
||||||
|
jne hf_damage_done
|
||||||
|
mov ebx,amount
|
||||||
|
mov eax,ebx
|
||||||
|
shr eax,1
|
||||||
|
cmp eax,hfArmor
|
||||||
|
jbe @F
|
||||||
|
mov eax,hfArmor
|
||||||
|
@@:
|
||||||
|
sub hfArmor,eax
|
||||||
|
sub ebx,eax
|
||||||
|
sub hfHealth,ebx
|
||||||
|
mov hfHurt,10
|
||||||
|
invoke HFPlaySound,2
|
||||||
|
cmp hfHealth,0
|
||||||
|
jg hf_damage_done
|
||||||
|
mov hfHealth,0
|
||||||
|
mov hfState,1
|
||||||
|
mov hfFireHeld,0
|
||||||
|
hf_damage_done:
|
||||||
|
ret
|
||||||
|
HFDamagePlayer ENDP
|
||||||
|
|
||||||
|
HFDamageEnemy PROC uses esi enemy:DWORD,amount:DWORD
|
||||||
|
mov esi,enemy
|
||||||
|
cmp [esi].HFEnemy.hp,0
|
||||||
|
jle hf_enemy_damage_done
|
||||||
|
mov eax,amount
|
||||||
|
sub [esi].HFEnemy.hp,eax
|
||||||
|
mov [esi].HFEnemy.awake,1
|
||||||
|
mov [esi].HFEnemy.pose,6
|
||||||
|
mov [esi].HFEnemy.timer,4
|
||||||
|
cmp [esi].HFEnemy.hp,0
|
||||||
|
jg hf_enemy_damage_done
|
||||||
|
mov [esi].HFEnemy.hp,0
|
||||||
|
mov [esi].HFEnemy.pose,7
|
||||||
|
mov [esi].HFEnemy.timer,0
|
||||||
|
inc hfKills
|
||||||
|
hf_enemy_damage_done:
|
||||||
|
ret
|
||||||
|
HFDamageEnemy ENDP
|
||||||
|
|
||||||
|
HFFire PROC uses esi edi ebx
|
||||||
|
local pellet:DWORD,angle:DWORD,rx:DWORD,ry:DWORD,stepX:DWORD,stepY:DWORD,steps:DWORD
|
||||||
|
cmp hfCooldown,0
|
||||||
|
jne hf_fire_done
|
||||||
|
cmp hfWeapon,1
|
||||||
|
je hf_fire_shotgun
|
||||||
|
cmp hfBullets,0
|
||||||
|
je hf_fire_done
|
||||||
|
dec hfBullets
|
||||||
|
mov hfCooldown,9
|
||||||
|
mov pellet,1
|
||||||
|
invoke HFPlaySound,4
|
||||||
|
jmp hf_fire_begin
|
||||||
|
hf_fire_shotgun:
|
||||||
|
cmp hfShells,0
|
||||||
|
je hf_fire_done
|
||||||
|
dec hfShells
|
||||||
|
mov hfCooldown,29
|
||||||
|
mov pellet,HF_SHOT_PELLETS
|
||||||
|
invoke HFPlaySound,0
|
||||||
|
hf_fire_begin:
|
||||||
|
mov hfFlash,4
|
||||||
|
hf_fire_pellet:
|
||||||
|
mov eax,playerAngle
|
||||||
|
cmp hfWeapon,0
|
||||||
|
je @F
|
||||||
|
mov ecx,pellet
|
||||||
|
add eax,hfPelletSpread[ecx*4-4]
|
||||||
|
@@:
|
||||||
|
and eax,511
|
||||||
|
mov angle,eax
|
||||||
|
mov ecx,tableCos[eax*4]
|
||||||
|
sar ecx,3
|
||||||
|
mov stepX,ecx
|
||||||
|
mov ecx,tableSin[eax*4]
|
||||||
|
sar ecx,3
|
||||||
|
mov stepY,ecx
|
||||||
|
mov eax,playerX
|
||||||
|
mov rx,eax
|
||||||
|
mov eax,playerY
|
||||||
|
mov ry,eax
|
||||||
|
mov steps,192
|
||||||
|
hf_ray_step:
|
||||||
|
mov eax,stepX
|
||||||
|
add rx,eax
|
||||||
|
mov eax,stepY
|
||||||
|
add ry,eax
|
||||||
|
mov eax,rx
|
||||||
|
sar eax,16
|
||||||
|
cmp eax,32
|
||||||
|
jae hf_ray_end
|
||||||
|
mov ecx,ry
|
||||||
|
sar ecx,16
|
||||||
|
cmp ecx,32
|
||||||
|
jae hf_ray_end
|
||||||
|
shl ecx,5
|
||||||
|
add eax,ecx
|
||||||
|
cmp hfMap[eax],0
|
||||||
|
jne hf_ray_end
|
||||||
|
mov esi,OFFSET hfEnemies
|
||||||
|
mov ebx,HF_ENEMIES
|
||||||
|
hf_ray_enemy:
|
||||||
|
cmp [esi].HFEnemy.hp,0
|
||||||
|
jle hf_ray_next_enemy
|
||||||
|
invoke HFDistance,rx,ry,[esi].HFEnemy.x,[esi].HFEnemy.y
|
||||||
|
cmp eax,FP_1*7/20
|
||||||
|
ja hf_ray_next_enemy
|
||||||
|
invoke RandomRange3D,9
|
||||||
|
add eax,8
|
||||||
|
invoke HFDamageEnemy,esi,eax
|
||||||
|
jmp hf_ray_end
|
||||||
|
hf_ray_next_enemy:
|
||||||
|
add esi,SIZEOF HFEnemy
|
||||||
|
dec ebx
|
||||||
|
jnz hf_ray_enemy
|
||||||
|
dec steps
|
||||||
|
jnz hf_ray_step
|
||||||
|
hf_ray_end:
|
||||||
|
; Each pellet owns its own impact; stay just in front of a wall's depth.
|
||||||
|
mov ecx,pellet
|
||||||
|
dec ecx
|
||||||
|
imul ecx,SIZEOF HFImpact
|
||||||
|
mov eax,rx
|
||||||
|
sub eax,stepX
|
||||||
|
sub eax,stepX
|
||||||
|
mov hfImpacts[ecx].x,eax
|
||||||
|
mov eax,ry
|
||||||
|
sub eax,stepY
|
||||||
|
sub eax,stepY
|
||||||
|
mov hfImpacts[ecx].y,eax
|
||||||
|
mov hfImpacts[ecx].life,8
|
||||||
|
dec pellet
|
||||||
|
jnz hf_fire_pellet
|
||||||
|
hf_fire_done:
|
||||||
|
ret
|
||||||
|
HFFire ENDP
|
||||||
|
|
||||||
|
HFSpawnBolt PROC uses esi ebx x:DWORD,y:DWORD,damage:DWORD
|
||||||
|
local distance:DWORD
|
||||||
|
mov esi,OFFSET hfBolts
|
||||||
|
mov ebx,32
|
||||||
|
@@:
|
||||||
|
cmp [esi].HFBolt.life,0
|
||||||
|
je hf_bolt_slot
|
||||||
|
add esi,SIZEOF HFBolt
|
||||||
|
dec ebx
|
||||||
|
jnz @B
|
||||||
|
ret
|
||||||
|
hf_bolt_slot:
|
||||||
|
invoke HFDistance,x,y,playerX,playerY
|
||||||
|
cmp eax,FP_1/2
|
||||||
|
jae @F
|
||||||
|
mov eax,FP_1/2
|
||||||
|
@@:
|
||||||
|
mov distance,eax
|
||||||
|
mov eax,x
|
||||||
|
mov [esi].HFBolt.x,eax
|
||||||
|
mov eax,y
|
||||||
|
mov [esi].HFBolt.y,eax
|
||||||
|
mov eax,damage
|
||||||
|
mov [esi].HFBolt.damage,eax
|
||||||
|
mov [esi].HFBolt.life,210
|
||||||
|
mov eax,playerX
|
||||||
|
sub eax,x
|
||||||
|
mov ecx,FP_1/7
|
||||||
|
imul ecx
|
||||||
|
idiv distance
|
||||||
|
mov [esi].HFBolt.vx,eax
|
||||||
|
mov eax,playerY
|
||||||
|
sub eax,y
|
||||||
|
mov ecx,FP_1/7
|
||||||
|
imul ecx
|
||||||
|
idiv distance
|
||||||
|
mov [esi].HFBolt.vy,eax
|
||||||
|
invoke HFPlaySound,1
|
||||||
|
ret
|
||||||
|
HFSpawnBolt ENDP
|
||||||
|
|
||||||
|
; Breadth-first distance field permits monsters to navigate around room pillars.
|
||||||
|
HFBuildFlow PROC uses esi edi ebx
|
||||||
|
local head:DWORD,tail:DWORD,current:DWORD,neighbor:DWORD
|
||||||
|
cld
|
||||||
|
mov edi,OFFSET hfFlow
|
||||||
|
mov eax,0FFFFFFFFh
|
||||||
|
mov ecx,512
|
||||||
|
rep stosd
|
||||||
|
mov eax,playerY
|
||||||
|
sar eax,16
|
||||||
|
shl eax,5
|
||||||
|
mov ecx,playerX
|
||||||
|
sar ecx,16
|
||||||
|
add eax,ecx
|
||||||
|
mov hfFlow[eax*2],0
|
||||||
|
mov hfQueue,eax
|
||||||
|
mov head,0
|
||||||
|
mov tail,1
|
||||||
|
hf_flow_pop:
|
||||||
|
mov eax,head
|
||||||
|
cmp eax,tail
|
||||||
|
jae hf_flow_done
|
||||||
|
mov eax,hfQueue[eax*4]
|
||||||
|
mov current,eax
|
||||||
|
movzx ebx,hfFlow[eax*2]
|
||||||
|
inc ebx
|
||||||
|
xor esi,esi
|
||||||
|
hf_flow_neighbor:
|
||||||
|
mov eax,current
|
||||||
|
cmp esi,0
|
||||||
|
jne @F
|
||||||
|
dec eax
|
||||||
|
@@:
|
||||||
|
cmp esi,1
|
||||||
|
jne @F
|
||||||
|
inc eax
|
||||||
|
@@:
|
||||||
|
cmp esi,2
|
||||||
|
jne @F
|
||||||
|
sub eax,32
|
||||||
|
@@:
|
||||||
|
cmp esi,3
|
||||||
|
jne @F
|
||||||
|
add eax,32
|
||||||
|
@@:
|
||||||
|
cmp eax,1024
|
||||||
|
jae hf_flow_next
|
||||||
|
cmp hfMap[eax],0
|
||||||
|
jne hf_flow_next
|
||||||
|
cmp hfFlow[eax*2],0FFFFh
|
||||||
|
jne hf_flow_next
|
||||||
|
mov hfFlow[eax*2],bx
|
||||||
|
mov ecx,tail
|
||||||
|
mov hfQueue[ecx*4],eax
|
||||||
|
inc tail
|
||||||
|
hf_flow_next:
|
||||||
|
inc esi
|
||||||
|
cmp esi,4
|
||||||
|
jb hf_flow_neighbor
|
||||||
|
inc head
|
||||||
|
jmp hf_flow_pop
|
||||||
|
hf_flow_done:
|
||||||
|
ret
|
||||||
|
HFBuildFlow ENDP
|
||||||
|
|
||||||
|
HFMoveEnemy PROC uses esi edi ebx enemy:DWORD
|
||||||
|
local tx:DWORD,ty:DWORD,distance:DWORD,speed:DWORD,best:DWORD,cell:DWORD,nx:DWORD,ny:DWORD
|
||||||
|
mov esi,enemy
|
||||||
|
mov eax,playerX
|
||||||
|
mov tx,eax
|
||||||
|
mov eax,playerY
|
||||||
|
mov ty,eax
|
||||||
|
invoke HFLineClear,[esi].HFEnemy.x,[esi].HFEnemy.y,playerX,playerY
|
||||||
|
test eax,eax
|
||||||
|
jnz hf_move_target
|
||||||
|
mov eax,[esi].HFEnemy.y
|
||||||
|
sar eax,16
|
||||||
|
shl eax,5
|
||||||
|
mov ecx,[esi].HFEnemy.x
|
||||||
|
sar ecx,16
|
||||||
|
add eax,ecx
|
||||||
|
mov cell,eax
|
||||||
|
movzx eax,hfFlow[eax*2]
|
||||||
|
mov best,eax
|
||||||
|
mov edi,-1
|
||||||
|
xor ebx,ebx
|
||||||
|
hf_move_flow:
|
||||||
|
mov eax,cell
|
||||||
|
cmp ebx,0
|
||||||
|
jne @F
|
||||||
|
dec eax
|
||||||
|
@@:
|
||||||
|
cmp ebx,1
|
||||||
|
jne @F
|
||||||
|
inc eax
|
||||||
|
@@:
|
||||||
|
cmp ebx,2
|
||||||
|
jne @F
|
||||||
|
sub eax,32
|
||||||
|
@@:
|
||||||
|
cmp ebx,3
|
||||||
|
jne @F
|
||||||
|
add eax,32
|
||||||
|
@@:
|
||||||
|
cmp eax,1024
|
||||||
|
jae hf_move_flow_next
|
||||||
|
movzx ecx,hfFlow[eax*2]
|
||||||
|
cmp ecx,best
|
||||||
|
jae hf_move_flow_next
|
||||||
|
mov best,ecx
|
||||||
|
mov edi,eax
|
||||||
|
hf_move_flow_next:
|
||||||
|
inc ebx
|
||||||
|
cmp ebx,4
|
||||||
|
jb hf_move_flow
|
||||||
|
cmp edi,-1
|
||||||
|
je hf_move_done
|
||||||
|
mov eax,edi
|
||||||
|
and eax,31
|
||||||
|
shl eax,16
|
||||||
|
add eax,FP_HALF
|
||||||
|
mov tx,eax
|
||||||
|
shr edi,5
|
||||||
|
shl edi,16
|
||||||
|
add edi,FP_HALF
|
||||||
|
mov ty,edi
|
||||||
|
hf_move_target:
|
||||||
|
invoke HFDistance,[esi].HFEnemy.x,[esi].HFEnemy.y,tx,ty
|
||||||
|
cmp eax,FP_1/8
|
||||||
|
jb hf_move_done
|
||||||
|
mov distance,eax
|
||||||
|
mov eax,[esi].HFEnemy.kind
|
||||||
|
mov eax,hfMonsterSpeed[eax*4]
|
||||||
|
mov speed,eax
|
||||||
|
mov eax,tx
|
||||||
|
sub eax,[esi].HFEnemy.x
|
||||||
|
imul speed
|
||||||
|
idiv distance
|
||||||
|
add eax,[esi].HFEnemy.x
|
||||||
|
mov nx,eax
|
||||||
|
invoke CanWalk3D,eax,[esi].HFEnemy.y
|
||||||
|
test eax,eax
|
||||||
|
jz @F
|
||||||
|
mov eax,nx
|
||||||
|
mov [esi].HFEnemy.x,eax
|
||||||
|
@@:
|
||||||
|
mov eax,ty
|
||||||
|
sub eax,[esi].HFEnemy.y
|
||||||
|
imul speed
|
||||||
|
idiv distance
|
||||||
|
add eax,[esi].HFEnemy.y
|
||||||
|
mov ny,eax
|
||||||
|
invoke CanWalk3D,[esi].HFEnemy.x,eax
|
||||||
|
test eax,eax
|
||||||
|
jz hf_move_done
|
||||||
|
mov eax,ny
|
||||||
|
mov [esi].HFEnemy.y,eax
|
||||||
|
hf_move_done:
|
||||||
|
ret
|
||||||
|
HFMoveEnemy ENDP
|
||||||
|
|
||||||
|
HFEnemiesTick PROC uses esi edi ebx
|
||||||
|
local distance:DWORD,visible:DWORD
|
||||||
|
mov esi,OFFSET hfEnemies
|
||||||
|
mov edi,HF_ENEMIES
|
||||||
|
hf_ai_enemy:
|
||||||
|
cmp [esi].HFEnemy.hp,0
|
||||||
|
jg hf_ai_alive
|
||||||
|
cmp [esi].HFEnemy.pose,11
|
||||||
|
jae hf_ai_next
|
||||||
|
inc [esi].HFEnemy.timer
|
||||||
|
cmp [esi].HFEnemy.timer,6
|
||||||
|
jb hf_ai_next
|
||||||
|
mov [esi].HFEnemy.timer,0
|
||||||
|
inc [esi].HFEnemy.pose
|
||||||
|
jmp hf_ai_next
|
||||||
|
hf_ai_alive:
|
||||||
|
cmp [esi].HFEnemy.cooldown,0
|
||||||
|
je @F
|
||||||
|
dec [esi].HFEnemy.cooldown
|
||||||
|
@@:
|
||||||
|
cmp [esi].HFEnemy.timer,0
|
||||||
|
je hf_ai_perceive
|
||||||
|
dec [esi].HFEnemy.timer
|
||||||
|
jnz hf_ai_next
|
||||||
|
cmp [esi].HFEnemy.pose,4
|
||||||
|
jne hf_ai_next
|
||||||
|
invoke HFLineClear,[esi].HFEnemy.x,[esi].HFEnemy.y,playerX,playerY
|
||||||
|
test eax,eax
|
||||||
|
jz hf_ai_next
|
||||||
|
invoke HFDistance,[esi].HFEnemy.x,[esi].HFEnemy.y,playerX,playerY
|
||||||
|
mov distance,eax
|
||||||
|
mov ebx,[esi].HFEnemy.kind
|
||||||
|
cmp eax,FP_1*7/10
|
||||||
|
jbe hf_ai_melee
|
||||||
|
cmp ebx,1
|
||||||
|
je hf_ai_next
|
||||||
|
cmp ebx,0
|
||||||
|
jne hf_ai_projectile
|
||||||
|
invoke RandomRange3D,4
|
||||||
|
test eax,eax
|
||||||
|
jz hf_ai_attack_pose
|
||||||
|
invoke HFDamagePlayer,6
|
||||||
|
jmp hf_ai_attack_pose
|
||||||
|
hf_ai_perceive:
|
||||||
|
invoke HFDistance,[esi].HFEnemy.x,[esi].HFEnemy.y,playerX,playerY
|
||||||
|
mov distance,eax
|
||||||
|
mov visible,0
|
||||||
|
cmp eax,16*FP_1
|
||||||
|
ja hf_ai_awake
|
||||||
|
invoke HFLineClear,[esi].HFEnemy.x,[esi].HFEnemy.y,playerX,playerY
|
||||||
|
mov visible,eax
|
||||||
|
test eax,eax
|
||||||
|
jz hf_ai_awake
|
||||||
|
mov [esi].HFEnemy.awake,1
|
||||||
|
hf_ai_awake:
|
||||||
|
cmp [esi].HFEnemy.awake,0
|
||||||
|
je hf_ai_next
|
||||||
|
mov eax,hfTicks
|
||||||
|
shr eax,3
|
||||||
|
and eax,3
|
||||||
|
mov [esi].HFEnemy.pose,eax
|
||||||
|
cmp visible,0
|
||||||
|
je hf_ai_move
|
||||||
|
mov ebx,[esi].HFEnemy.kind
|
||||||
|
cmp [esi].HFEnemy.cooldown,0
|
||||||
|
jne hf_ai_spacing
|
||||||
|
cmp distance,FP_1*7/10
|
||||||
|
jbe hf_ai_windup
|
||||||
|
cmp ebx,1
|
||||||
|
je hf_ai_move
|
||||||
|
hf_ai_windup:
|
||||||
|
mov [esi].HFEnemy.pose,4
|
||||||
|
mov [esi].HFEnemy.timer,12
|
||||||
|
jmp hf_ai_next
|
||||||
|
hf_ai_projectile:
|
||||||
|
invoke HFSpawnBolt,[esi].HFEnemy.x,[esi].HFEnemy.y,hfMonsterDamage[ebx*4]
|
||||||
|
jmp hf_ai_attack_pose
|
||||||
|
hf_ai_melee:
|
||||||
|
invoke HFDamagePlayer,hfMonsterDamage[ebx*4]
|
||||||
|
hf_ai_attack_pose:
|
||||||
|
mov [esi].HFEnemy.pose,5
|
||||||
|
mov [esi].HFEnemy.timer,10
|
||||||
|
mov [esi].HFEnemy.cooldown,55
|
||||||
|
cmp ebx,3
|
||||||
|
jne hf_ai_next
|
||||||
|
mov [esi].HFEnemy.cooldown,18
|
||||||
|
jmp hf_ai_next
|
||||||
|
hf_ai_spacing:
|
||||||
|
cmp distance,FP_1*7/10
|
||||||
|
jb hf_ai_next
|
||||||
|
cmp ebx,1
|
||||||
|
je hf_ai_move
|
||||||
|
cmp distance,4*FP_1
|
||||||
|
jb hf_ai_next
|
||||||
|
hf_ai_move:
|
||||||
|
invoke HFMoveEnemy,esi
|
||||||
|
hf_ai_next:
|
||||||
|
add esi,SIZEOF HFEnemy
|
||||||
|
dec edi
|
||||||
|
jnz hf_ai_enemy
|
||||||
|
ret
|
||||||
|
HFEnemiesTick ENDP
|
||||||
|
|
||||||
|
HFBoltsTick PROC uses esi edi
|
||||||
|
mov esi,OFFSET hfBolts
|
||||||
|
mov edi,32
|
||||||
|
hf_projectile:
|
||||||
|
cmp [esi].HFBolt.life,0
|
||||||
|
je hf_projectile_next
|
||||||
|
dec [esi].HFBolt.life
|
||||||
|
mov eax,[esi].HFBolt.vx
|
||||||
|
add [esi].HFBolt.x,eax
|
||||||
|
mov eax,[esi].HFBolt.vy
|
||||||
|
add [esi].HFBolt.y,eax
|
||||||
|
invoke CanWalk3D,[esi].HFBolt.x,[esi].HFBolt.y
|
||||||
|
test eax,eax
|
||||||
|
jz hf_projectile_destroy
|
||||||
|
invoke HFDistance,[esi].HFBolt.x,[esi].HFBolt.y,playerX,playerY
|
||||||
|
cmp eax,FP_1*3/10
|
||||||
|
ja hf_projectile_next
|
||||||
|
invoke HFDamagePlayer,[esi].HFBolt.damage
|
||||||
|
hf_projectile_destroy:
|
||||||
|
mov [esi].HFBolt.life,0
|
||||||
|
hf_projectile_next:
|
||||||
|
add esi,SIZEOF HFBolt
|
||||||
|
dec edi
|
||||||
|
jnz hf_projectile
|
||||||
|
ret
|
||||||
|
HFBoltsTick ENDP
|
||||||
|
|
||||||
|
HFPickups PROC uses esi edi ebx
|
||||||
|
xor ebx,ebx
|
||||||
|
mov esi,OFFSET hfItemSeeds
|
||||||
|
hf_pickup:
|
||||||
|
cmp hfItemTaken[ebx*4],0
|
||||||
|
jne hf_pickup_next
|
||||||
|
invoke HFDistance,playerX,playerY,[esi],[esi+4]
|
||||||
|
cmp eax,FP_1*3/5
|
||||||
|
ja hf_pickup_next
|
||||||
|
mov edi,[esi+8]
|
||||||
|
cmp edi,0
|
||||||
|
jne hf_pick_armor
|
||||||
|
cmp hfHealth,100
|
||||||
|
jae hf_pickup_next
|
||||||
|
add hfHealth,25
|
||||||
|
cmp hfHealth,100
|
||||||
|
jbe hf_pick_commit
|
||||||
|
mov hfHealth,100
|
||||||
|
jmp hf_pick_commit
|
||||||
|
hf_pick_armor:
|
||||||
|
cmp edi,1
|
||||||
|
jne hf_pick_bullets
|
||||||
|
cmp hfArmor,100
|
||||||
|
jae hf_pickup_next
|
||||||
|
mov hfArmor,100
|
||||||
|
jmp hf_pick_commit
|
||||||
|
hf_pick_bullets:
|
||||||
|
cmp edi,2
|
||||||
|
jne hf_pick_shells
|
||||||
|
add hfBullets,40
|
||||||
|
jmp hf_pick_commit
|
||||||
|
hf_pick_shells:
|
||||||
|
cmp edi,3
|
||||||
|
jne hf_pick_shotgun
|
||||||
|
add hfShells,12
|
||||||
|
jmp hf_pick_commit
|
||||||
|
hf_pick_shotgun:
|
||||||
|
cmp edi,4
|
||||||
|
jne hf_pick_key
|
||||||
|
mov hfHasShotgun,1
|
||||||
|
mov hfWeapon,1
|
||||||
|
add hfShells,16
|
||||||
|
jmp hf_pick_commit
|
||||||
|
hf_pick_key:
|
||||||
|
mov hfKey,1
|
||||||
|
hf_pick_commit:
|
||||||
|
mov hfItemTaken[ebx*4],1
|
||||||
|
mov hfMessage,OFFSET hfPickupText
|
||||||
|
cmp edi,5
|
||||||
|
jne @F
|
||||||
|
mov hfMessage,OFFSET hfKeyFoundText
|
||||||
|
@@:
|
||||||
|
mov hfMessageTime,105
|
||||||
|
invoke HFPlaySound,3
|
||||||
|
hf_pickup_next:
|
||||||
|
add esi,12
|
||||||
|
inc ebx
|
||||||
|
cmp ebx,HF_ITEMS
|
||||||
|
jb hf_pickup
|
||||||
|
ret
|
||||||
|
HFPickups ENDP
|
||||||
|
|
||||||
|
HFUse PROC uses ebx esi
|
||||||
|
invoke HFDistance,playerX,playerY,FP_1*11/2,FP_1*25/2
|
||||||
|
cmp eax,FP_1*8/5
|
||||||
|
ja hf_use_secret_two
|
||||||
|
test hfDoorMask,1
|
||||||
|
jnz hf_use_secret_two
|
||||||
|
or hfDoorMask,1
|
||||||
|
mov hfMap[12*32+5],0
|
||||||
|
mov hfMap[12*32+6],0
|
||||||
|
jmp hf_use_secret
|
||||||
|
hf_use_secret_two:
|
||||||
|
invoke HFDistance,playerX,playerY,FP_1*45/2,27*FP_1
|
||||||
|
cmp eax,FP_1*8/5
|
||||||
|
ja hf_use_gate
|
||||||
|
test hfDoorMask,2
|
||||||
|
jnz hf_use_gate
|
||||||
|
or hfDoorMask,2
|
||||||
|
mov hfMap[26*32+22],0
|
||||||
|
mov hfMap[27*32+22],0
|
||||||
|
hf_use_secret:
|
||||||
|
inc hfSecrets
|
||||||
|
mov hfMessage,OFFSET hfSecretText
|
||||||
|
mov hfMessageTime,105
|
||||||
|
invoke HFPlaySound,5
|
||||||
|
ret
|
||||||
|
hf_use_gate:
|
||||||
|
invoke HFDistance,playerX,playerY,FP_1*45/2,7*FP_1
|
||||||
|
cmp eax,2*FP_1
|
||||||
|
ja hf_use_exit
|
||||||
|
cmp hfKey,0
|
||||||
|
jne hf_use_unlock
|
||||||
|
mov hfMessage,OFFSET hfNeedKeyText
|
||||||
|
mov hfMessageTime,105
|
||||||
|
ret
|
||||||
|
hf_use_unlock:
|
||||||
|
mov hfMap[6*32+22],0
|
||||||
|
mov hfMap[7*32+22],0
|
||||||
|
or hfDoorMask,4
|
||||||
|
mov hfMessage,OFFSET hfDoorText
|
||||||
|
mov hfMessageTime,105
|
||||||
|
invoke HFPlaySound,5
|
||||||
|
ret
|
||||||
|
hf_use_exit:
|
||||||
|
invoke HFDistance,playerX,playerY,28*FP_1,29*FP_1
|
||||||
|
cmp eax,2*FP_1
|
||||||
|
ja hf_use_done
|
||||||
|
mov esi,OFFSET hfEnemies
|
||||||
|
mov ebx,HF_ENEMIES
|
||||||
|
@@:
|
||||||
|
cmp [esi].HFEnemy.kind,3
|
||||||
|
jne hf_exit_next
|
||||||
|
cmp [esi].HFEnemy.hp,0
|
||||||
|
jg hf_exit_locked
|
||||||
|
hf_exit_next:
|
||||||
|
add esi,SIZEOF HFEnemy
|
||||||
|
dec ebx
|
||||||
|
jnz @B
|
||||||
|
mov hfState,2
|
||||||
|
mov hfFireHeld,0
|
||||||
|
mov hfMessageTime,0
|
||||||
|
cmp hfCompleted,0
|
||||||
|
jne hf_use_done
|
||||||
|
mov hfCompleted,1
|
||||||
|
mov hfAwarded,1
|
||||||
|
invoke ArtifactCredit3D,6660
|
||||||
|
call SaveProgress3D
|
||||||
|
ret
|
||||||
|
hf_exit_locked:
|
||||||
|
mov hfMessage,OFFSET hfExitText
|
||||||
|
mov hfMessageTime,105
|
||||||
|
hf_use_done:
|
||||||
|
ret
|
||||||
|
HFUse ENDP
|
||||||
|
|
||||||
|
HFTick PROC uses esi ecx
|
||||||
|
mov esi,OFFSET hfImpacts
|
||||||
|
mov ecx,HF_SHOT_PELLETS
|
||||||
|
hf_impact_tick:
|
||||||
|
cmp [esi].HFImpact.life,0
|
||||||
|
je @F
|
||||||
|
dec [esi].HFImpact.life
|
||||||
|
@@:
|
||||||
|
add esi,SIZEOF HFImpact
|
||||||
|
loop hf_impact_tick
|
||||||
|
inc hfTicks
|
||||||
|
cmp hfCooldown,0
|
||||||
|
je @F
|
||||||
|
dec hfCooldown
|
||||||
|
@@:
|
||||||
|
cmp hfFlash,0
|
||||||
|
je @F
|
||||||
|
dec hfFlash
|
||||||
|
@@:
|
||||||
|
cmp hfHurt,0
|
||||||
|
je @F
|
||||||
|
dec hfHurt
|
||||||
|
@@:
|
||||||
|
cmp hfMessageTime,0
|
||||||
|
je @F
|
||||||
|
dec hfMessageTime
|
||||||
|
@@:
|
||||||
|
mov eax,hfFireHeld
|
||||||
|
or eax,hfFirePending
|
||||||
|
or al,keys3D[20h]
|
||||||
|
jz @F
|
||||||
|
call HFFire
|
||||||
|
@@:
|
||||||
|
mov hfFirePending,0
|
||||||
|
cmp hfUsePending,0
|
||||||
|
je @F
|
||||||
|
mov hfUsePending,0
|
||||||
|
call HFUse
|
||||||
|
@@:
|
||||||
|
cmp hfState,0
|
||||||
|
jne hf_tick_done
|
||||||
|
mov eax,hfTicks
|
||||||
|
and eax,7
|
||||||
|
jnz @F
|
||||||
|
call HFBuildFlow
|
||||||
|
@@:
|
||||||
|
call HFEnemiesTick
|
||||||
|
call HFBoltsTick
|
||||||
|
cmp hfState,0
|
||||||
|
jne hf_tick_done
|
||||||
|
call HFPickups
|
||||||
|
; Central lava channel: visible floor patch exactly matches damage bounds.
|
||||||
|
mov eax,hfTicks
|
||||||
|
xor edx,edx
|
||||||
|
mov ecx,18
|
||||||
|
div ecx
|
||||||
|
test edx,edx
|
||||||
|
jnz hf_tick_done
|
||||||
|
cmp playerX,16*FP_1
|
||||||
|
jb hf_tick_done
|
||||||
|
cmp playerX,18*FP_1
|
||||||
|
jae hf_tick_done
|
||||||
|
cmp playerY,17*FP_1
|
||||||
|
jb hf_tick_done
|
||||||
|
cmp playerY,20*FP_1
|
||||||
|
jae hf_tick_done
|
||||||
|
invoke HFDamagePlayer,8
|
||||||
|
hf_tick_done:
|
||||||
|
ret
|
||||||
|
HFTick ENDP
|
||||||
203
src_graphics/hell_fps_data.inc
Normal file
203
src_graphics/hell_fps_data.inc
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
; Native MASM32 hell expedition. Assets: Freedoom 0.13.0 (assets/hell/COPYING.txt).
|
||||||
|
MODE_HELL_FPS equ 29
|
||||||
|
INTERACT_HELL_FPS equ 55
|
||||||
|
HF_WALL equ 185
|
||||||
|
HF_FLOOR equ 191
|
||||||
|
HF_CEILING equ 192
|
||||||
|
HF_LAVA equ 193
|
||||||
|
; South wall faces the customer side of the bar at x=3..9, y=23..24.
|
||||||
|
HF_PORTAL_X equ 6*FP_1
|
||||||
|
HF_PORTAL_Y equ FP_1*309/10
|
||||||
|
HF_PAINTING_MATERIAL equ 500
|
||||||
|
HF_PACK_COUNT equ 15
|
||||||
|
HF_SHOT_PELLETS equ 7
|
||||||
|
HF_MUSIC_SCALE equ 35 ; settings 0..10 -> MCI 0..350 (35% of previous level)
|
||||||
|
BeginHellFPS3D PROTO
|
||||||
|
LeaveHellFPS3D PROTO
|
||||||
|
HandleHellFPS3D PROTO
|
||||||
|
RenderHellFPS3D PROTO
|
||||||
|
RenderHellPortal3D PROTO
|
||||||
|
FreeHellFPS3D PROTO
|
||||||
|
HFReset PROTO
|
||||||
|
HFBuildFlow PROTO
|
||||||
|
HFTick PROTO
|
||||||
|
HFFire PROTO
|
||||||
|
HFUse PROTO
|
||||||
|
HFLineClear PROTO :DWORD,:DWORD,:DWORD,:DWORD
|
||||||
|
HFDamagePlayer PROTO :DWORD
|
||||||
|
HFDamageEnemy PROTO :DWORD,:DWORD
|
||||||
|
HFSpawnBolt PROTO :DWORD,:DWORD,:DWORD
|
||||||
|
HFDistance PROTO :DWORD,:DWORD,:DWORD,:DWORD
|
||||||
|
HFPlayerCanWalk PROTO :DWORD,:DWORD
|
||||||
|
HFPlaySound PROTO :DWORD
|
||||||
|
HFUpdateMusicVolume PROTO
|
||||||
|
HFEnemy STRUCT
|
||||||
|
x dd ?
|
||||||
|
y dd ?
|
||||||
|
kind dd ?
|
||||||
|
hp dd ?
|
||||||
|
cooldown dd ?
|
||||||
|
pose dd ?
|
||||||
|
timer dd ?
|
||||||
|
awake dd ?
|
||||||
|
HFEnemy ENDS
|
||||||
|
HFBolt STRUCT
|
||||||
|
x dd ?
|
||||||
|
y dd ?
|
||||||
|
vx dd ?
|
||||||
|
vy dd ?
|
||||||
|
life dd ?
|
||||||
|
damage dd ?
|
||||||
|
HFBolt ENDS
|
||||||
|
HFImpact STRUCT
|
||||||
|
x dd ?
|
||||||
|
y dd ?
|
||||||
|
life dd ?
|
||||||
|
HFImpact ENDS
|
||||||
|
include src_graphics\hell_fps_map.inc
|
||||||
|
.data
|
||||||
|
hfCompleted dd 0
|
||||||
|
hfState dd 0 ; 0 playing, 1 dead, 2 victory
|
||||||
|
hfHealth dd 100
|
||||||
|
hfArmor dd 0
|
||||||
|
hfBullets dd 80
|
||||||
|
hfShells dd 0
|
||||||
|
hfWeapon dd 0
|
||||||
|
hfHasShotgun dd 0
|
||||||
|
hfKey dd 0
|
||||||
|
hfSecrets dd 0
|
||||||
|
hfKills dd 0
|
||||||
|
hfTicks dd 0
|
||||||
|
hfAccumulator dd 0
|
||||||
|
hfCooldown dd 0
|
||||||
|
hfFlash dd 0
|
||||||
|
hfHurt dd 0
|
||||||
|
hfFireHeld dd 0
|
||||||
|
hfFirePending dd 0
|
||||||
|
hfUsePending dd 0
|
||||||
|
hfOldX dd 0
|
||||||
|
hfOldY dd 0
|
||||||
|
hfOldAngle dd 0
|
||||||
|
hfOldMap dd 0
|
||||||
|
hfOldFloor dd 0
|
||||||
|
hfOldLevel dd 0
|
||||||
|
hfOldEye dd 0
|
||||||
|
hfOldCeiling dd 0
|
||||||
|
hfDoorMask dd 0
|
||||||
|
hfMessage dd 0
|
||||||
|
hfMessageTime dd 0
|
||||||
|
hfLoaded dd 0
|
||||||
|
hfSoundReady dd 0
|
||||||
|
hfMusicError dd 0
|
||||||
|
hfSoundFailures dd 0
|
||||||
|
hfVolumeError dd 0
|
||||||
|
hfNumber db 32 dup(0)
|
||||||
|
hfTitle db 'HELL: THE HOUSE ALWAYS BLEEDS',0
|
||||||
|
hfHelp db 'WASD MOVE MOUSE AIM LMB FIRE 1/2 WEAPON E USE ESC RETURN',0
|
||||||
|
hfHealthText db 'HEALTH',0
|
||||||
|
hfArmorText db 'ARMOR',0
|
||||||
|
hfAmmoText db 'AMMO',0
|
||||||
|
hfKillsText db 'KILLS',0
|
||||||
|
hfKeyText db 'BLUE KEY',0
|
||||||
|
hfPistolText db 'PISTOL',0
|
||||||
|
hfShotgunText db 'SHOTGUN',0
|
||||||
|
hfDeadText db 'YOU DIED - ENTER: RETRY / ESC: CASINO',0
|
||||||
|
hfWinText db 'HELL CLEARED - ENTER / ESC: CASINO',0
|
||||||
|
hfRewardText db 'FIRST CLEAR: +$6,660',0
|
||||||
|
hfRepeatText db 'EXPEDITION COMPLETE',0
|
||||||
|
hfAwarded dd 0
|
||||||
|
hfStatsText db 'TIME (SEC) KILLS / 28 SECRETS / 2',0
|
||||||
|
hfPortalPrompt db '[E] HELL EXPEDITION',0
|
||||||
|
hfMissingText db 'HELL ASSETS MISSING - RESTORE assets/hell',0
|
||||||
|
hfNeedKeyText db 'THE SANCTUM NEEDS THE BLUE KEY',0
|
||||||
|
hfDoorText db 'THE GATE OPENS',0
|
||||||
|
hfSecretText db 'SECRET FOUND',0
|
||||||
|
hfPickupText db 'SUPPLIES COLLECTED',0
|
||||||
|
hfKeyFoundText db 'BLUE KEY ACQUIRED - FIND THE EASTERN GATE',0
|
||||||
|
hfExitText db 'DEFEAT THE WARDEN, THEN USE THE EXIT PORTAL',0
|
||||||
|
hfWelcomeText db 'FIND THE SHOTGUN. RECOVER THE BLUE KEY. KILL THE WARDEN.',0
|
||||||
|
hfMaterials AnimPack3D <>
|
||||||
|
hfSoldier AnimPack3D <>
|
||||||
|
hfDemon AnimPack3D <>
|
||||||
|
hfImp AnimPack3D <>
|
||||||
|
hfBoss AnimPack3D <>
|
||||||
|
hfPistol AnimPack3D <>
|
||||||
|
hfShotgun AnimPack3D <>
|
||||||
|
hfItemsArt AnimPack3D <>
|
||||||
|
hfFireArt AnimPack3D <>
|
||||||
|
hfPortalArt AnimPack3D <>
|
||||||
|
hfHUDArt AnimPack3D <>
|
||||||
|
hfDigitsArt AnimPack3D <>
|
||||||
|
hfFacesArt AnimPack3D <>
|
||||||
|
hfPaintingArt AnimPack3D <>
|
||||||
|
hfImpactsArt AnimPack3D <>
|
||||||
|
hfPacks dd OFFSET hfMaterials,OFFSET hfSoldier,OFFSET hfDemon,OFFSET hfImp,OFFSET hfBoss,OFFSET hfPistol,OFFSET hfShotgun,OFFSET hfItemsArt,OFFSET hfFireArt,OFFSET hfPortalArt,OFFSET hfHUDArt,OFFSET hfDigitsArt,OFFSET hfFacesArt,OFFSET hfPaintingArt,OFFSET hfImpactsArt
|
||||||
|
hfMatPath db 'assets\hell\materials.bin',0
|
||||||
|
hfSoldierPath db 'assets\hell\soldier.bin',0
|
||||||
|
hfDemonPath db 'assets\hell\demon.bin',0
|
||||||
|
hfImpPath db 'assets\hell\imp.bin',0
|
||||||
|
hfBossPath db 'assets\hell\boss.bin',0
|
||||||
|
hfPistolPath db 'assets\hell\pistol.bin',0
|
||||||
|
hfShotgunPath db 'assets\hell\shotgun.bin',0
|
||||||
|
hfItemsPath db 'assets\hell\items.bin',0
|
||||||
|
hfFirePath db 'assets\hell\fire.bin',0
|
||||||
|
hfPortalPath db 'assets\hell\portal.bin',0
|
||||||
|
hfHUDPath db 'assets\hell\hud.bin',0
|
||||||
|
hfDigitsPath db 'assets\hell\digits.bin',0
|
||||||
|
hfFacesPath db 'assets\hell\faces.bin',0
|
||||||
|
hfPaintingPath db 'assets\hell\painting.bin',0
|
||||||
|
hfImpactsPath db 'assets\hell\impacts.bin',0
|
||||||
|
hfPaths dd OFFSET hfMatPath,OFFSET hfSoldierPath,OFFSET hfDemonPath,OFFSET hfImpPath,OFFSET hfBossPath,OFFSET hfPistolPath,OFFSET hfShotgunPath,OFFSET hfItemsPath,OFFSET hfFirePath,OFFSET hfPortalPath,OFFSET hfHUDPath,OFFSET hfDigitsPath,OFFSET hfFacesPath,OFFSET hfPaintingPath,OFFSET hfImpactsPath
|
||||||
|
hfMonsterPacks dd OFFSET hfSoldier,OFFSET hfDemon,OFFSET hfImp,OFFSET hfBoss
|
||||||
|
hfMonsterHP dd 55,105,85,1100
|
||||||
|
hfMonsterSpeed dd 1900,3300,1800,1350
|
||||||
|
hfMonsterDamage dd 6,15,10,18
|
||||||
|
hfObject CityProp3D <0,0,FP_1,FP_1*3/2,0,OFFSET hfSoldier,0>
|
||||||
|
hfPelletSpread dd -9,-6,-3,0,3,6,9 ; independent rays, ~12.7-degree full cone
|
||||||
|
hfPercentText db '%',0
|
||||||
|
hfArmsText db 'ARMS',0
|
||||||
|
hfSlot1Text db '1',0
|
||||||
|
hfSlot2Text db '2',0
|
||||||
|
hfAmmoTypesText db 'BULL',0
|
||||||
|
hfShellTypeText db 'SHEL',0
|
||||||
|
hfEmptyAmmoText db 'RCKT 0 50',0
|
||||||
|
hfEmptyCellText db 'CELL 0 300',0
|
||||||
|
hfMaxBulletsText db '/ 280',0
|
||||||
|
hfMaxShellsText db '/ 160',0
|
||||||
|
hfMusicOpen db 'open "assets\music\tower\Mick_Gordon_-_The_Only_Thing_They_Fear_Is_You_OST_DOOM_Eternal_72020713.mp3" type mpegvideo alias hellmusic',0
|
||||||
|
hfMusicPlay db 'play hellmusic repeat',0
|
||||||
|
hfMusicClose db 'close hellmusic',0
|
||||||
|
hfVolumePrefix db 'setaudio hellmusic volume to ',0
|
||||||
|
hfVolumeCommand db 80 dup(0)
|
||||||
|
hfLastVolume dd -1
|
||||||
|
; Six independent waveaudio channels let combat, pain and pickups overlap.
|
||||||
|
hfSndOpen0 db 'open "assets\hell\dsshotgn.wav" type waveaudio alias hfshot',0
|
||||||
|
hfSndOpen1 db 'open "assets\hell\dsfirsht.wav" type waveaudio alias hfmonster',0
|
||||||
|
hfSndOpen2 db 'open "assets\hell\dsplpain.wav" type waveaudio alias hfhurt',0
|
||||||
|
hfSndOpen3 db 'open "assets\hell\dsitemup.wav" type waveaudio alias hfpick',0
|
||||||
|
hfSndOpen4 db 'open "assets\hell\dspistol.wav" type waveaudio alias hfpistol',0
|
||||||
|
hfSndOpen5 db 'open "assets\hell\dsdoropn.wav" type waveaudio alias hfdoor',0
|
||||||
|
hfSndPlay0 db 'play hfshot from 0',0
|
||||||
|
hfSndPlay1 db 'play hfmonster from 0',0
|
||||||
|
hfSndPlay2 db 'play hfhurt from 0',0
|
||||||
|
hfSndPlay3 db 'play hfpick from 0',0
|
||||||
|
hfSndPlay4 db 'play hfpistol from 0',0
|
||||||
|
hfSndPlay5 db 'play hfdoor from 0',0
|
||||||
|
hfSndClose0 db 'close hfshot',0
|
||||||
|
hfSndClose1 db 'close hfmonster',0
|
||||||
|
hfSndClose2 db 'close hfhurt',0
|
||||||
|
hfSndClose3 db 'close hfpick',0
|
||||||
|
hfSndClose4 db 'close hfpistol',0
|
||||||
|
hfSndClose5 db 'close hfdoor',0
|
||||||
|
hfSndOpens dd OFFSET hfSndOpen0,OFFSET hfSndOpen1,OFFSET hfSndOpen2,OFFSET hfSndOpen3,OFFSET hfSndOpen4,OFFSET hfSndOpen5
|
||||||
|
hfSndPlays dd OFFSET hfSndPlay0,OFFSET hfSndPlay1,OFFSET hfSndPlay2,OFFSET hfSndPlay3,OFFSET hfSndPlay4,OFFSET hfSndPlay5
|
||||||
|
hfSndCloses dd OFFSET hfSndClose0,OFFSET hfSndClose1,OFFSET hfSndClose2,OFFSET hfSndClose3,OFFSET hfSndClose4,OFFSET hfSndClose5
|
||||||
|
.data?
|
||||||
|
hfMap db 1024 dup(?)
|
||||||
|
hfEnemies HFEnemy HF_ENEMIES dup(<>)
|
||||||
|
hfItemTaken dd HF_ITEMS dup(?)
|
||||||
|
hfBolts HFBolt 32 dup(<>)
|
||||||
|
hfFlow dw 1024 dup(?)
|
||||||
|
hfQueue dd 1024 dup(?)
|
||||||
|
hfImpacts HFImpact HF_SHOT_PELLETS dup(<>)
|
||||||
|
.code
|
||||||
92
src_graphics/hell_fps_map.inc
Normal file
92
src_graphics/hell_fps_map.inc
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
; Generated by tools/generators/make_hell_level.py.
|
||||||
|
.data
|
||||||
|
hfMapTemplate label byte
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,186,186,186,186,186,186,186,186,186,186,186,187,187,187,187,187,187,187,187,185
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,186,186,186,186,186,186,186,186,186,186,186,187,187,187,187,187,187,187,187,185
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,186,0,0,0,0,0,0,0,0,0,186,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,186,0,0,0,0,0,0,0,0,0,186,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,186,0,0,0,0,0,0,0,0,0,186,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,186,0,0,0,186,0,0,0,0,0,188,0,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,185,185,185,186,0,0,0,0,0,0,186,0,0,188,0,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,185,185,185,186,0,0,0,0,0,0,0,0,0,186,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,185,185,185,186,0,0,0,0,0,0,0,0,0,186,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,185,185,185,186,0,0,0,0,0,0,0,0,0,186,187,0,0,186,0,0,0,187,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,185,185,185,186,186,186,186,186,0,0,186,186,186,186,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,185,185,188,188,185,185,185,185,185,185,185,185,185,185,0,0,185,185,185,185,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,185,185,0,0,185,185,185,185,185,0,0,0,0,0,0,0,0,0,0,185,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,0,0,0,0,0,0,0,0,185,0,0,0,0,0,0,0,0,0,0,185,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,0,0,0,0,0,0,0,0,185,0,0,0,0,0,0,0,0,0,0,185,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,0,0,0,0,0,0,0,0,0,0,0,0,186,0,0,0,0,0,0,185,187,0,0,0,186,0,0,187,185
|
||||||
|
db 185,185,185,0,0,186,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,0,0,0,0,0,186,0,0,185,0,0,0,0,0,0,0,0,0,0,185,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,0,0,0,0,0,0,0,0,185,0,0,0,0,0,0,0,186,0,0,185,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,0,0,0,0,0,0,0,0,185,0,0,0,0,0,0,0,0,0,0,185,187,0,0,0,0,0,0,187,185
|
||||||
|
db 185,185,185,0,0,0,0,0,0,0,0,185,0,0,0,0,0,0,0,0,0,0,185,187,187,187,0,0,187,187,187,185
|
||||||
|
db 185,185,185,185,185,0,0,185,185,185,185,185,0,0,0,0,0,0,0,0,0,0,185,190,190,190,0,0,190,190,190,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,0,0,185,185,185,185,185,185,185,185,185,185,185,185,0,0,0,0,0,0,0,190,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,0,0,185,185,185,185,185,185,185,185,185,185,185,185,0,0,0,0,0,0,0,190,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,0,0,185,185,185,185,185,185,185,0,0,0,0,185,0,0,0,0,0,0,0,190,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,0,0,185,185,185,185,185,185,185,0,0,0,0,188,0,0,186,0,0,0,0,190,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,0,0,185,185,185,185,185,185,185,0,0,0,0,188,0,0,0,0,0,0,0,190,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,0,0,185,185,185,185,185,185,185,0,0,0,0,185,0,0,0,0,0,0,0,190,185
|
||||||
|
db 185,185,0,0,0,0,0,0,0,0,0,185,185,185,185,185,185,185,185,185,185,185,185,0,0,0,0,0,0,0,190,185
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,190,190,190,190,190,190,190,190,185
|
||||||
|
db 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185
|
||||||
|
hfEnemySeeds label dword
|
||||||
|
dd 7*FP_1+FP_HALF,25*FP_1+FP_HALF,0
|
||||||
|
dd 8*FP_1+FP_HALF,28*FP_1+FP_HALF,0
|
||||||
|
dd 4*FP_1+FP_HALF,20*FP_1+FP_HALF,0
|
||||||
|
dd 8*FP_1+FP_HALF,16*FP_1+FP_HALF,1
|
||||||
|
dd 9*FP_1+FP_HALF,20*FP_1+FP_HALF,2
|
||||||
|
dd 4*FP_1+FP_HALF,15*FP_1+FP_HALF,0
|
||||||
|
dd 14*FP_1+FP_HALF,20*FP_1+FP_HALF,2
|
||||||
|
dd 20*FP_1+FP_HALF,21*FP_1+FP_HALF,0
|
||||||
|
dd 20*FP_1+FP_HALF,14*FP_1+FP_HALF,2
|
||||||
|
dd 13*FP_1+FP_HALF,14*FP_1+FP_HALF,1
|
||||||
|
dd 18*FP_1+FP_HALF,17*FP_1+FP_HALF,1
|
||||||
|
dd 15*FP_1+FP_HALF,18*FP_1+FP_HALF,0
|
||||||
|
dd 14*FP_1+FP_HALF,8*FP_1+FP_HALF,2
|
||||||
|
dd 20*FP_1+FP_HALF,4*FP_1+FP_HALF,2
|
||||||
|
dd 15*FP_1+FP_HALF,4*FP_1+FP_HALF,0
|
||||||
|
dd 20*FP_1+FP_HALF,9*FP_1+FP_HALF,1
|
||||||
|
dd 18*FP_1+FP_HALF,5*FP_1+FP_HALF,0
|
||||||
|
dd 25*FP_1+FP_HALF,5*FP_1+FP_HALF,0
|
||||||
|
dd 28*FP_1+FP_HALF,8*FP_1+FP_HALF,2
|
||||||
|
dd 25*FP_1+FP_HALF,12*FP_1+FP_HALF,1
|
||||||
|
dd 28*FP_1+FP_HALF,15*FP_1+FP_HALF,0
|
||||||
|
dd 25*FP_1+FP_HALF,19*FP_1+FP_HALF,2
|
||||||
|
dd 28*FP_1+FP_HALF,19*FP_1+FP_HALF,1
|
||||||
|
dd 24*FP_1+FP_HALF,25*FP_1+FP_HALF,2
|
||||||
|
dd 28*FP_1+FP_HALF,25*FP_1+FP_HALF,2
|
||||||
|
dd 27*FP_1+FP_HALF,28*FP_1+FP_HALF,3
|
||||||
|
dd 4*FP_1+FP_HALF,9*FP_1+FP_HALF,1
|
||||||
|
dd 7*FP_1+FP_HALF,8*FP_1+FP_HALF,0
|
||||||
|
HF_ENEMIES equ 28
|
||||||
|
hfItemSeeds label dword
|
||||||
|
dd 3*FP_1+FP_HALF,27*FP_1+FP_HALF,4
|
||||||
|
dd 4*FP_1+FP_HALF,28*FP_1+FP_HALF,3
|
||||||
|
dd 9*FP_1+FP_HALF,24*FP_1+FP_HALF,2
|
||||||
|
dd 3*FP_1+FP_HALF,19*FP_1+FP_HALF,0
|
||||||
|
dd 8*FP_1+FP_HALF,14*FP_1+FP_HALF,3
|
||||||
|
dd 9*FP_1+FP_HALF,15*FP_1+FP_HALF,2
|
||||||
|
dd 13*FP_1+FP_HALF,21*FP_1+FP_HALF,1
|
||||||
|
dd 14*FP_1+FP_HALF,14*FP_1+FP_HALF,3
|
||||||
|
dd 20*FP_1+FP_HALF,20*FP_1+FP_HALF,0
|
||||||
|
dd 19*FP_1+FP_HALF,14*FP_1+FP_HALF,2
|
||||||
|
dd 14*FP_1+FP_HALF,4*FP_1+FP_HALF,0
|
||||||
|
dd 20*FP_1+FP_HALF,3*FP_1+FP_HALF,5
|
||||||
|
dd 17*FP_1+FP_HALF,9*FP_1+FP_HALF,3
|
||||||
|
dd 25*FP_1+FP_HALF,4*FP_1+FP_HALF,2
|
||||||
|
dd 28*FP_1+FP_HALF,11*FP_1+FP_HALF,0
|
||||||
|
dd 25*FP_1+FP_HALF,17*FP_1+FP_HALF,3
|
||||||
|
dd 28*FP_1+FP_HALF,20*FP_1+FP_HALF,3
|
||||||
|
dd 24*FP_1+FP_HALF,24*FP_1+FP_HALF,0
|
||||||
|
dd 29*FP_1+FP_HALF,28*FP_1+FP_HALF,3
|
||||||
|
dd 24*FP_1+FP_HALF,28*FP_1+FP_HALF,2
|
||||||
|
dd 3*FP_1+FP_HALF,8*FP_1+FP_HALF,1
|
||||||
|
dd 7*FP_1+FP_HALF,10*FP_1+FP_HALF,3
|
||||||
|
dd 19*FP_1+FP_HALF,26*FP_1+FP_HALF,0
|
||||||
|
dd 20*FP_1+FP_HALF,27*FP_1+FP_HALF,3
|
||||||
|
HF_ITEMS equ 24
|
||||||
|
.code
|
||||||
348
src_graphics/hell_fps_render.inc
Normal file
348
src_graphics/hell_fps_render.inc
Normal file
|
|
@ -0,0 +1,348 @@
|
||||||
|
; World sprites share casino depth testing. HUD uses native 640x480 pixels.
|
||||||
|
HFText PROC uses esi edi ebx x:DWORD,y:DWORD,text:DWORD,color:DWORD
|
||||||
|
local row:DWORD,column:DWORD,cursor:DWORD,bits:DWORD
|
||||||
|
mov esi,text
|
||||||
|
mov eax,x
|
||||||
|
mov cursor,eax
|
||||||
|
hf_text_char:
|
||||||
|
movzx eax,BYTE PTR [esi]
|
||||||
|
test eax,eax
|
||||||
|
jz hf_text_done
|
||||||
|
lea edi,font8x8[eax*8]
|
||||||
|
mov row,0
|
||||||
|
hf_text_row:
|
||||||
|
mov eax,row
|
||||||
|
movzx eax,BYTE PTR [edi+eax]
|
||||||
|
mov bits,eax
|
||||||
|
mov column,0
|
||||||
|
hf_text_pixel:
|
||||||
|
test bits,80h
|
||||||
|
jz hf_text_next_pixel
|
||||||
|
mov eax,y
|
||||||
|
add eax,row
|
||||||
|
cmp eax,480
|
||||||
|
jae hf_text_next_pixel
|
||||||
|
mov ebx,cursor
|
||||||
|
add ebx,column
|
||||||
|
cmp ebx,640
|
||||||
|
jae hf_text_next_pixel
|
||||||
|
imul eax,640
|
||||||
|
add eax,ebx
|
||||||
|
mov ebx,color
|
||||||
|
mov screenBuffer[eax*4],ebx
|
||||||
|
hf_text_next_pixel:
|
||||||
|
shl bits,1
|
||||||
|
inc column
|
||||||
|
cmp column,8
|
||||||
|
jb hf_text_pixel
|
||||||
|
inc row
|
||||||
|
cmp row,8
|
||||||
|
jb hf_text_row
|
||||||
|
add cursor,8
|
||||||
|
inc esi
|
||||||
|
jmp hf_text_char
|
||||||
|
hf_text_done:
|
||||||
|
ret
|
||||||
|
HFText ENDP
|
||||||
|
|
||||||
|
HFNumber PROC x:DWORD,y:DWORD,value:DWORD
|
||||||
|
invoke FormatNumber,value,OFFSET hfNumber
|
||||||
|
invoke HFText,x,y,OFFSET hfNumber,00FFE0A0h
|
||||||
|
ret
|
||||||
|
HFNumber ENDP
|
||||||
|
|
||||||
|
HFLargeNumber PROC uses ebx edi rightEdge:DWORD,y:DWORD,value:DWORD,percent:DWORD
|
||||||
|
mov ebx,value
|
||||||
|
mov edi,rightEdge
|
||||||
|
cmp percent,0
|
||||||
|
je hf_large_digit
|
||||||
|
sub edi,28
|
||||||
|
invoke DrawAnimSprite3D,OFFSET hfDigitsArt,10,edi,y,32,32
|
||||||
|
hf_large_digit:
|
||||||
|
mov eax,ebx
|
||||||
|
xor edx,edx
|
||||||
|
mov ecx,10
|
||||||
|
div ecx
|
||||||
|
mov ebx,eax
|
||||||
|
sub edi,28
|
||||||
|
invoke DrawAnimSprite3D,OFFSET hfDigitsArt,edx,edi,y,32,32
|
||||||
|
test ebx,ebx
|
||||||
|
jnz hf_large_digit
|
||||||
|
ret
|
||||||
|
HFLargeNumber ENDP
|
||||||
|
|
||||||
|
HFRenderStatus PROC uses ebx
|
||||||
|
invoke DrawAnimSprite3D,OFFSET hfHUDArt,0,0,416,640,64
|
||||||
|
mov eax,hfBullets
|
||||||
|
cmp hfWeapon,0
|
||||||
|
je @F
|
||||||
|
mov eax,hfShells
|
||||||
|
@@:
|
||||||
|
invoke HFLargeNumber,86,422,eax,0
|
||||||
|
invoke HFLargeNumber,208,422,hfHealth,1
|
||||||
|
invoke HFLargeNumber,466,422,hfArmor,1
|
||||||
|
; Replace multiplayer FRAGS field with the owned weapon slots.
|
||||||
|
invoke DrawBox,108,209,35,31,003C3B36h
|
||||||
|
invoke HFText,228,460,OFFSET hfArmsText,00B0ACA0h
|
||||||
|
mov eax,00777770h
|
||||||
|
cmp hfWeapon,0
|
||||||
|
jne @F
|
||||||
|
mov eax,00FFE070h
|
||||||
|
@@:
|
||||||
|
invoke HFText,230,434,OFFSET hfSlot1Text,eax
|
||||||
|
cmp hfHasShotgun,0
|
||||||
|
je hf_status_face
|
||||||
|
mov eax,00777770h
|
||||||
|
cmp hfWeapon,1
|
||||||
|
jne @F
|
||||||
|
mov eax,00FFE070h
|
||||||
|
@@:
|
||||||
|
invoke HFText,258,434,OFFSET hfSlot2Text,eax
|
||||||
|
hf_status_face:
|
||||||
|
mov eax,100
|
||||||
|
sub eax,hfHealth
|
||||||
|
xor edx,edx
|
||||||
|
mov ecx,20
|
||||||
|
div ecx
|
||||||
|
cmp eax,4
|
||||||
|
jbe @F
|
||||||
|
mov eax,4
|
||||||
|
@@:
|
||||||
|
shl eax,2
|
||||||
|
mov ebx,eax
|
||||||
|
cmp hfHurt,0
|
||||||
|
je hf_face_idle
|
||||||
|
add ebx,3
|
||||||
|
jmp hf_face_draw
|
||||||
|
hf_face_idle:
|
||||||
|
mov eax,hfTicks
|
||||||
|
shr eax,5
|
||||||
|
xor edx,edx
|
||||||
|
mov ecx,3
|
||||||
|
div ecx
|
||||||
|
add ebx,edx
|
||||||
|
hf_face_draw:
|
||||||
|
cmp hfState,1
|
||||||
|
jne @F
|
||||||
|
mov ebx,20
|
||||||
|
@@:
|
||||||
|
invoke DrawAnimSprite3D,OFFSET hfFacesArt,ebx,296,416,48,64
|
||||||
|
cmp hfKey,0
|
||||||
|
je @F
|
||||||
|
invoke DrawAnimSprite3D,OFFSET hfItemsArt,5,476,420,28,28
|
||||||
|
@@:
|
||||||
|
; Clear original ammo glyphs, then draw live reserve counts.
|
||||||
|
invoke DrawBox,257,209,62,30,002E302Ch
|
||||||
|
invoke HFText,518,420,OFFSET hfAmmoTypesText,00B0ACA0h
|
||||||
|
invoke HFNumber,558,420,hfBullets
|
||||||
|
invoke HFText,590,420,OFFSET hfMaxBulletsText,008E8C80h
|
||||||
|
invoke HFText,518,434,OFFSET hfShellTypeText,00B0ACA0h
|
||||||
|
invoke HFNumber,558,434,hfShells
|
||||||
|
invoke HFText,590,434,OFFSET hfMaxShellsText,008E8C80h
|
||||||
|
invoke HFText,518,448,OFFSET hfEmptyAmmoText,008E8C80h
|
||||||
|
invoke HFText,518,462,OFFSET hfEmptyCellText,008E8C80h
|
||||||
|
ret
|
||||||
|
HFRenderStatus ENDP
|
||||||
|
|
||||||
|
RenderHellPortal3D PROC
|
||||||
|
cmp hallLevel3D,0
|
||||||
|
jne hf_portal_done
|
||||||
|
invoke LoadAnimPack3D,OFFSET hfPaintingArt,OFFSET hfPaintingPath
|
||||||
|
cmp hfPaintingArt.pixels,0
|
||||||
|
je hf_portal_done
|
||||||
|
; Gilt landscape hung flat on the south wall opposite the bar.
|
||||||
|
invoke DrawCityVertical3D,1,HF_PORTAL_Y,3*FP_1,9*FP_1,FP_1*35/100,FP_1*235/100,HF_PAINTING_MATERIAL
|
||||||
|
hf_portal_done:
|
||||||
|
ret
|
||||||
|
RenderHellPortal3D ENDP
|
||||||
|
|
||||||
|
RenderHellFPS3D PROC uses esi edi ebx
|
||||||
|
call UpdateCamera3D
|
||||||
|
call RenderCeilingAndFloor
|
||||||
|
call RenderWalls
|
||||||
|
call PrepareCityDepth3D
|
||||||
|
; Animated lava patch, same bounds as HFTick damage.
|
||||||
|
mov eax,hfTicks
|
||||||
|
shr eax,3
|
||||||
|
and eax,3
|
||||||
|
add eax,HF_LAVA
|
||||||
|
invoke DrawCityHorizontal3D,16*FP_1,18*FP_1,17*FP_1,20*FP_1,128,eax
|
||||||
|
mov esi,OFFSET hfEnemies
|
||||||
|
mov edi,HF_ENEMIES
|
||||||
|
hf_render_enemy:
|
||||||
|
mov eax,[esi].HFEnemy.x
|
||||||
|
mov hfObject.posX,eax
|
||||||
|
mov eax,[esi].HFEnemy.y
|
||||||
|
mov hfObject.posY,eax
|
||||||
|
mov eax,[esi].HFEnemy.kind
|
||||||
|
mov eax,hfMonsterPacks[eax*4]
|
||||||
|
mov hfObject.asset,eax
|
||||||
|
mov eax,[esi].HFEnemy.pose
|
||||||
|
mov hfObject.frame,eax
|
||||||
|
mov hfObject.worldW,FP_1*6/5
|
||||||
|
mov hfObject.worldH,FP_1*3/2
|
||||||
|
mov hfObject.groundZ,0
|
||||||
|
cmp [esi].HFEnemy.kind,3
|
||||||
|
jne @F
|
||||||
|
mov hfObject.worldW,FP_1*9/5
|
||||||
|
mov hfObject.worldH,FP_1*22/10
|
||||||
|
@@:
|
||||||
|
invoke DrawCityObject3D,OFFSET hfObject,0
|
||||||
|
add esi,SIZEOF HFEnemy
|
||||||
|
dec edi
|
||||||
|
jnz hf_render_enemy
|
||||||
|
mov hfObject.asset,OFFSET hfItemsArt
|
||||||
|
mov hfObject.worldW,FP_1/2
|
||||||
|
mov hfObject.worldH,FP_1/2
|
||||||
|
mov hfObject.groundZ,0
|
||||||
|
xor edi,edi
|
||||||
|
mov esi,OFFSET hfItemSeeds
|
||||||
|
hf_render_item:
|
||||||
|
cmp hfItemTaken[edi*4],0
|
||||||
|
jne hf_render_item_next
|
||||||
|
mov eax,[esi]
|
||||||
|
mov hfObject.posX,eax
|
||||||
|
mov eax,[esi+4]
|
||||||
|
mov hfObject.posY,eax
|
||||||
|
mov eax,[esi+8]
|
||||||
|
mov hfObject.frame,eax
|
||||||
|
invoke DrawCityObject3D,OFFSET hfObject,0
|
||||||
|
hf_render_item_next:
|
||||||
|
add esi,12
|
||||||
|
inc edi
|
||||||
|
cmp edi,HF_ITEMS
|
||||||
|
jb hf_render_item
|
||||||
|
mov hfObject.asset,OFFSET hfFireArt
|
||||||
|
mov hfObject.worldW,FP_1/3
|
||||||
|
mov hfObject.worldH,FP_1/3
|
||||||
|
mov hfObject.groundZ,FP_1*3/4
|
||||||
|
mov eax,hfTicks
|
||||||
|
shr eax,2
|
||||||
|
and eax,1
|
||||||
|
mov hfObject.frame,eax
|
||||||
|
mov esi,OFFSET hfBolts
|
||||||
|
mov edi,32
|
||||||
|
hf_render_bolt:
|
||||||
|
cmp [esi].HFBolt.life,0
|
||||||
|
je hf_render_bolt_next
|
||||||
|
mov eax,[esi].HFBolt.x
|
||||||
|
mov hfObject.posX,eax
|
||||||
|
mov eax,[esi].HFBolt.y
|
||||||
|
mov hfObject.posY,eax
|
||||||
|
invoke DrawCityObject3D,OFFSET hfObject,0
|
||||||
|
hf_render_bolt_next:
|
||||||
|
add esi,SIZEOF HFBolt
|
||||||
|
dec edi
|
||||||
|
jnz hf_render_bolt
|
||||||
|
mov hfObject.asset,OFFSET hfImpactsArt
|
||||||
|
mov hfObject.worldW,FP_1/3
|
||||||
|
mov hfObject.worldH,FP_1/3
|
||||||
|
mov hfObject.groundZ,FP_1*5/6
|
||||||
|
mov esi,OFFSET hfImpacts
|
||||||
|
mov edi,HF_SHOT_PELLETS
|
||||||
|
hf_render_impact:
|
||||||
|
cmp [esi].HFImpact.life,0
|
||||||
|
je hf_render_impact_next
|
||||||
|
mov eax,[esi].HFImpact.x
|
||||||
|
mov hfObject.posX,eax
|
||||||
|
mov eax,[esi].HFImpact.y
|
||||||
|
mov hfObject.posY,eax
|
||||||
|
mov eax,8
|
||||||
|
sub eax,[esi].HFImpact.life
|
||||||
|
shr eax,1
|
||||||
|
mov hfObject.frame,eax
|
||||||
|
invoke DrawCityObject3D,OFFSET hfObject,0
|
||||||
|
hf_render_impact_next:
|
||||||
|
add esi,SIZEOF HFImpact
|
||||||
|
dec edi
|
||||||
|
jnz hf_render_impact
|
||||||
|
mov hfObject.asset,OFFSET hfPortalArt
|
||||||
|
mov hfObject.posX,28*FP_1
|
||||||
|
mov hfObject.posY,29*FP_1
|
||||||
|
mov hfObject.worldW,FP_1*3/2
|
||||||
|
mov hfObject.worldH,2*FP_1
|
||||||
|
mov hfObject.groundZ,0
|
||||||
|
mov eax,hfTicks
|
||||||
|
shr eax,2
|
||||||
|
and eax,7
|
||||||
|
mov hfObject.frame,eax
|
||||||
|
invoke DrawCityObject3D,OFFSET hfObject,0
|
||||||
|
cmp hfState,0
|
||||||
|
jne hf_render_hud
|
||||||
|
mov esi,OFFSET hfPistol
|
||||||
|
mov edi,0
|
||||||
|
cmp hfWeapon,1
|
||||||
|
jne hf_weapon_pose
|
||||||
|
mov esi,OFFSET hfShotgun
|
||||||
|
hf_weapon_pose:
|
||||||
|
cmp hfCooldown,0
|
||||||
|
je hf_weapon_draw
|
||||||
|
cmp hfWeapon,0
|
||||||
|
jne hf_shotgun_pose
|
||||||
|
; Four recovery poses over the nine-tick pistol cycle.
|
||||||
|
mov eax,9
|
||||||
|
sub eax,hfCooldown
|
||||||
|
shr eax,1
|
||||||
|
inc eax
|
||||||
|
cmp eax,4
|
||||||
|
jbe @F
|
||||||
|
mov eax,4
|
||||||
|
@@:
|
||||||
|
mov edi,eax
|
||||||
|
jmp hf_weapon_draw
|
||||||
|
hf_shotgun_pose:
|
||||||
|
mov edi,3
|
||||||
|
cmp hfCooldown,10
|
||||||
|
jbe hf_weapon_draw
|
||||||
|
mov edi,2
|
||||||
|
cmp hfCooldown,18
|
||||||
|
jbe hf_weapon_draw
|
||||||
|
mov edi,1
|
||||||
|
hf_weapon_draw:
|
||||||
|
invoke DrawAnimSprite3D,esi,edi,176,176,288,240
|
||||||
|
cmp hfFlash,0
|
||||||
|
je @F
|
||||||
|
invoke DrawAnimSprite3D,OFFSET hfFireArt,0,296,202,48,48
|
||||||
|
@@:
|
||||||
|
invoke DrawBox,159,117,2,7,00D0C0A0h
|
||||||
|
invoke DrawBox,156,120,8,1,00D0C0A0h
|
||||||
|
hf_render_hud:
|
||||||
|
cmp hfHurt,0
|
||||||
|
je @F
|
||||||
|
invoke DrawBox,0,0,320,3,00902010h
|
||||||
|
invoke DrawBox,0,0,3,210,00902010h
|
||||||
|
invoke DrawBox,317,0,3,210,00902010h
|
||||||
|
@@:
|
||||||
|
call HFRenderStatus
|
||||||
|
cmp hfMessageTime,0
|
||||||
|
je @F
|
||||||
|
invoke HFText,12,34,hfMessage,00FFD090h
|
||||||
|
@@:
|
||||||
|
cmp hfState,0
|
||||||
|
je hf_render_done
|
||||||
|
invoke DrawBox,24,80,272,65,00181010h
|
||||||
|
mov eax,OFFSET hfDeadText
|
||||||
|
cmp hfState,1
|
||||||
|
je @F
|
||||||
|
mov eax,OFFSET hfWinText
|
||||||
|
@@:
|
||||||
|
invoke HFText,64,176,eax,00FFB070h
|
||||||
|
invoke HFText,64,208,OFFSET hfStatsText,00C0A080h
|
||||||
|
mov eax,hfTicks
|
||||||
|
xor edx,edx
|
||||||
|
mov ecx,35
|
||||||
|
div ecx
|
||||||
|
invoke HFNumber,80,226,eax
|
||||||
|
invoke HFNumber,232,226,hfKills
|
||||||
|
invoke HFNumber,392,226,hfSecrets
|
||||||
|
cmp hfState,2
|
||||||
|
jne hf_render_done
|
||||||
|
mov eax,OFFSET hfRepeatText
|
||||||
|
cmp hfAwarded,0
|
||||||
|
je @F
|
||||||
|
mov eax,OFFSET hfRewardText
|
||||||
|
@@:
|
||||||
|
invoke HFText,144,258,eax,00FFE0A0h
|
||||||
|
hf_render_done:
|
||||||
|
ret
|
||||||
|
RenderHellFPS3D ENDP
|
||||||
|
|
@ -14,6 +14,19 @@ WindowEvents3D PROC uses ebx esi edi hwnd:DWORD, uMsg:DWORD, wp:DWORD, lp:DWORD
|
||||||
xor eax, eax
|
xor eax, eax
|
||||||
ret
|
ret
|
||||||
@@:
|
@@:
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
jne hf_window_not_mouse
|
||||||
|
cmp uMsg,201h
|
||||||
|
jne @F
|
||||||
|
mov hfFireHeld,1
|
||||||
|
mov hfFirePending,1
|
||||||
|
jmp event_done
|
||||||
|
@@:
|
||||||
|
cmp uMsg,202h
|
||||||
|
jne hf_window_not_mouse
|
||||||
|
mov hfFireHeld,0
|
||||||
|
jmp event_done
|
||||||
|
hf_window_not_mouse:
|
||||||
cmp uMsg,202h
|
cmp uMsg,202h
|
||||||
jne @F
|
jne @F
|
||||||
cmp engineMode,MODE_DARTS
|
cmp engineMode,MODE_DARTS
|
||||||
|
|
@ -150,6 +163,8 @@ event_fullscreen:
|
||||||
call ToggleFullscreen3D
|
call ToggleFullscreen3D
|
||||||
jmp event_done
|
jmp event_done
|
||||||
event_focus_lost:
|
event_focus_lost:
|
||||||
|
mov hfFireHeld,0
|
||||||
|
mov hfFirePending,0
|
||||||
mov brEntryHold3D,0
|
mov brEntryHold3D,0
|
||||||
mov dtHolding3D,0
|
mov dtHolding3D,0
|
||||||
mov dtMouseReleased3D,0
|
mov dtMouseReleased3D,0
|
||||||
|
|
@ -512,6 +527,8 @@ UpdateCamera3D ENDP
|
||||||
|
|
||||||
; Four corners of a 0.3-tile-wide body; bounds checked before every map read.
|
; Four corners of a 0.3-tile-wide body; bounds checked before every map read.
|
||||||
CanWalk3D PROC uses ebx esi edi px:DWORD, py:DWORD
|
CanWalk3D PROC uses ebx esi edi px:DWORD, py:DWORD
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
je br_generic_collision
|
||||||
cmp brActive3D,0
|
cmp brActive3D,0
|
||||||
jne br_generic_collision
|
jne br_generic_collision
|
||||||
invoke CanWalkHall3D, px, py
|
invoke CanWalkHall3D, px, py
|
||||||
|
|
@ -560,6 +577,7 @@ LoadProgress3D PROC uses ebx
|
||||||
mov charmSkullUses, 0
|
mov charmSkullUses, 0
|
||||||
mov charmDevilUses, 0
|
mov charmDevilUses, 0
|
||||||
mov charmEnabledMask3D,7
|
mov charmEnabledMask3D,7
|
||||||
|
mov hfCompleted,0
|
||||||
mov brOwned3D,0
|
mov brOwned3D,0
|
||||||
mov brEnabled3D,1
|
mov brEnabled3D,1
|
||||||
invoke CreateFileA, OFFSET savePath3D, 80000000h, 1, 0, 3, 80h, 0
|
invoke CreateFileA, OFFSET savePath3D, 80000000h, 1, 0, 3, 80h, 0
|
||||||
|
|
@ -578,7 +596,7 @@ LoadProgress3D PROC uses ebx
|
||||||
cmp eax,saveData3D[36]
|
cmp eax,saveData3D[36]
|
||||||
jne progress_loaded
|
jne progress_loaded
|
||||||
mov eax,saveData3D[32]
|
mov eax,saveData3D[32]
|
||||||
test eax,0FFFCFFF0h
|
test eax,0FFF8FFF0h
|
||||||
jnz progress_loaded
|
jnz progress_loaded
|
||||||
test eax,20000h
|
test eax,20000h
|
||||||
jz @F
|
jz @F
|
||||||
|
|
@ -595,6 +613,10 @@ LoadProgress3D PROC uses ebx
|
||||||
shr eax,17
|
shr eax,17
|
||||||
and eax,1
|
and eax,1
|
||||||
mov brEnabled3D,eax
|
mov brEnabled3D,eax
|
||||||
|
mov eax,saveData3D[32]
|
||||||
|
shr eax,18
|
||||||
|
and eax,1
|
||||||
|
mov hfCompleted,eax
|
||||||
jmp progress_version_ok
|
jmp progress_version_ok
|
||||||
progress_legacy_version:
|
progress_legacy_version:
|
||||||
cmp saveData3D, 33443343h
|
cmp saveData3D, 33443343h
|
||||||
|
|
@ -690,6 +712,10 @@ SaveProgress3D PROC uses ebx
|
||||||
shl ecx,17
|
shl ecx,17
|
||||||
or eax,ecx
|
or eax,ecx
|
||||||
or eax,charmEnabledMask3D
|
or eax,charmEnabledMask3D
|
||||||
|
mov ecx,hfCompleted
|
||||||
|
and ecx,1
|
||||||
|
shl ecx,18
|
||||||
|
or eax,ecx
|
||||||
mov saveData3D[32],eax
|
mov saveData3D[32],eax
|
||||||
invoke PreviewArtifactSignature3D,OFFSET saveData3D
|
invoke PreviewArtifactSignature3D,OFFSET saveData3D
|
||||||
mov saveData3D[36],eax
|
mov saveData3D[36],eax
|
||||||
|
|
|
||||||
237
tools/generators/import_freedoom.py
Normal file
237
tools/generators/import_freedoom.py
Normal file
|
|
@ -0,0 +1,237 @@
|
||||||
|
"""Import pinned Freedoom assets into native ANM1 packs (offline build tool)."""
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import struct
|
||||||
|
import urllib.request
|
||||||
|
import wave
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
SHA256 = "3f9b264f3e3ce503b4fb7f6bdcb1f419d93c7b546f4df3e874dd878db9688f59"
|
||||||
|
URL = "https://github.com/freedoom/freedoom/releases/download/v0.13.0/freedoom-0.13.0.zip"
|
||||||
|
|
||||||
|
|
||||||
|
class Wad:
|
||||||
|
def __init__(self, data):
|
||||||
|
magic, count, offset = struct.unpack_from('<4sII', data)
|
||||||
|
assert magic == b'IWAD'
|
||||||
|
self.lumps = {}
|
||||||
|
for i in range(count):
|
||||||
|
pos, size, name = struct.unpack_from('<II8s', data, offset + i * 16)
|
||||||
|
assert pos + size <= len(data)
|
||||||
|
self.lumps[name.rstrip(b'\0').decode()] = data[pos:pos + size]
|
||||||
|
self.palette = self.lumps['PLAYPAL'][:768]
|
||||||
|
|
||||||
|
def patch(self, name):
|
||||||
|
data = self.lumps[name]
|
||||||
|
w, h, left, top = struct.unpack_from('<HHhh', data)
|
||||||
|
image = Image.new('RGBA', (w, h))
|
||||||
|
pixels = image.load()
|
||||||
|
for x in range(w):
|
||||||
|
pos = struct.unpack_from('<I', data, 8 + x * 4)[0]
|
||||||
|
previous = -1
|
||||||
|
while data[pos] != 255:
|
||||||
|
y, n = data[pos], data[pos + 1]
|
||||||
|
if y <= previous:
|
||||||
|
y += previous
|
||||||
|
previous = y
|
||||||
|
for dy, index in enumerate(data[pos + 3:pos + 3 + n]):
|
||||||
|
if y + dy < h:
|
||||||
|
pixels[x, y + dy] = (*self.palette[index * 3:index * 3 + 3], 255)
|
||||||
|
pos += n + 4
|
||||||
|
return image, left, top
|
||||||
|
|
||||||
|
def flat(self, name):
|
||||||
|
image = Image.frombytes('P', (64, 64), self.lumps[name])
|
||||||
|
image.putpalette(self.palette)
|
||||||
|
return image.convert('RGBA')
|
||||||
|
|
||||||
|
def texture(self, name):
|
||||||
|
pnames = self.lumps['PNAMES']
|
||||||
|
patches = [pnames[4+i*8:12+i*8].rstrip(b'\0').decode()
|
||||||
|
for i in range(struct.unpack_from('<I', pnames)[0])]
|
||||||
|
for table in ('TEXTURE1', 'TEXTURE2'):
|
||||||
|
data = self.lumps.get(table)
|
||||||
|
if not data:
|
||||||
|
continue
|
||||||
|
for i in range(struct.unpack_from('<I', data)[0]):
|
||||||
|
pos = struct.unpack_from('<I', data, 4+i*4)[0]
|
||||||
|
texname = data[pos:pos+8].rstrip(b'\0').decode()
|
||||||
|
if texname != name:
|
||||||
|
continue
|
||||||
|
w, h = struct.unpack_from('<HH', data, pos+12)
|
||||||
|
image = Image.new('RGBA', (w, h), (0, 0, 0, 255))
|
||||||
|
count = struct.unpack_from('<H', data, pos+20)[0]
|
||||||
|
for j in range(count):
|
||||||
|
x, y, patch = struct.unpack_from('<hhH', data, pos+22+j*10)
|
||||||
|
sprite, _, _ = self.patch(patches[patch])
|
||||||
|
image.paste(sprite, (x, y), sprite)
|
||||||
|
return image
|
||||||
|
raise KeyError(name)
|
||||||
|
|
||||||
|
|
||||||
|
def pack(path, images):
|
||||||
|
w, h = images[0].size
|
||||||
|
assert all(im.size == (w, h) for im in images)
|
||||||
|
payload = b''.join(im.tobytes('raw', 'BGRA') for im in images)
|
||||||
|
path.write_bytes(struct.pack('<5I', 0x314d4e41, w, h, len(images), len(payload)) + payload)
|
||||||
|
|
||||||
|
|
||||||
|
def aligned(wad, names, size=(128, 128)):
|
||||||
|
frames = [wad.patch(n) for n in names]
|
||||||
|
# Shared Doom origin, shared scale: no per-frame trim/resize jitter.
|
||||||
|
x0 = min(-left for _, left, _ in frames)
|
||||||
|
x1 = max(im.width-left for im, left, _ in frames)
|
||||||
|
y0 = min(-top for _, _, top in frames)
|
||||||
|
y1 = max(im.height-top for im, _, top in frames)
|
||||||
|
scale = min(size[0] / (x1-x0), size[1] / (y1-y0))
|
||||||
|
result = []
|
||||||
|
for im, left, top in frames:
|
||||||
|
canvas = Image.new('RGBA', size)
|
||||||
|
im = im.resize((max(1, round(im.width*scale)), max(1, round(im.height*scale))), Image.Resampling.NEAREST)
|
||||||
|
x = round((-left-x0)*scale + (size[0]-(x1-x0)*scale)/2)
|
||||||
|
y = round((-top-y0)*scale + size[1]-(y1-y0)*scale)
|
||||||
|
canvas.paste(im, (x, y))
|
||||||
|
result.append(canvas)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def interface_assets(wad, out, manifest):
|
||||||
|
"""Doom-layout status bar, native-size glyphs and common-origin face frames."""
|
||||||
|
pack(out/'hud.bin', [wad.patch('STBAR')[0]])
|
||||||
|
names = [f'STTNUM{i}' for i in range(10)] + ['STTPRCNT']
|
||||||
|
digits = []
|
||||||
|
for name in names:
|
||||||
|
image = wad.patch(name)[0]
|
||||||
|
canvas = Image.new('RGBA', (16,16))
|
||||||
|
canvas.paste(image, (16-image.width,0))
|
||||||
|
digits.append(canvas)
|
||||||
|
pack(out/'digits.bin', digits)
|
||||||
|
names_face = [name for health in range(5)
|
||||||
|
for name in [f'STFST{health}0', f'STFST{health}1',
|
||||||
|
f'STFST{health}2', f'STFOUCH{health}']] + ['STFDEAD0']
|
||||||
|
faces = []
|
||||||
|
for name in names_face:
|
||||||
|
image = wad.patch(name)[0]
|
||||||
|
canvas = Image.new('RGBA', (24,32))
|
||||||
|
canvas.paste(image, ((24-image.width)//2,32-image.height))
|
||||||
|
faces.append(canvas)
|
||||||
|
pack(out/'faces.bin', faces)
|
||||||
|
impacts = ['PUFFA0','PUFFB0','PUFFC0','PUFFD0']
|
||||||
|
pack(out/'impacts.bin', aligned(wad, impacts, (32,32)))
|
||||||
|
manifest['packs'].update(hud=['STBAR'], digits=names, faces=names_face, impacts=impacts)
|
||||||
|
|
||||||
|
# Authored landscape, entirely composed from Freedoom textures and sprites.
|
||||||
|
# Opaque power-of-two 512x256 BGRA surface; wall renderer wraps with bit masks.
|
||||||
|
image = Image.new('RGBA',(512,192),(20,9,8,255))
|
||||||
|
sky = wad.texture('SKY3').resize((488,166),Image.Resampling.NEAREST)
|
||||||
|
image.paste(sky,(12,12))
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
draw.polygon([(12,136),(65,98),(96,126),(139,86),(174,125),(222,104),
|
||||||
|
(273,142),(321,90),(376,123),(423,94),(500,130),(500,180),(12,180)],
|
||||||
|
fill=(34,20,22,255))
|
||||||
|
lava = wad.flat('LAVA1').resize((488,48),Image.Resampling.NEAREST)
|
||||||
|
image.paste(lava,(12,132))
|
||||||
|
for x,h in [(42,70),(92,52),(362,62),(428,84)]:
|
||||||
|
stone = wad.texture('GSTONE1').resize((28,h),Image.Resampling.NEAREST)
|
||||||
|
image.paste(stone,(x,164-h))
|
||||||
|
draw.rectangle((x-3,160-h,x+30,165-h),fill=(107,72,45,255))
|
||||||
|
for name,x,y,w,h in [('BOSSA1',224,48,70,116),('TROOA1',145,104,34,58),
|
||||||
|
('SARGA1',330,112,42,52)]:
|
||||||
|
actor = wad.patch(name)[0]
|
||||||
|
actor.thumbnail((w,h),Image.Resampling.NEAREST)
|
||||||
|
image.alpha_composite(actor,(x,y))
|
||||||
|
# Layered gilt moulding and dark inner bevel: a painting, not an open portal.
|
||||||
|
for inset,color in [(0,(42,25,12)),(2,(199,147,64)),(4,(104,61,22)),
|
||||||
|
(6,(236,191,99)),(8,(116,74,28)),(10,(26,13,8))]:
|
||||||
|
draw.rectangle((inset,inset,511-inset,191-inset),outline=(*color,255),width=2)
|
||||||
|
for x in range(18,495,16):
|
||||||
|
draw.line((x,4,x+3,7),fill=(255,216,126,255),width=1)
|
||||||
|
draw.line((x,184,x+3,187),fill=(255,216,126,255),width=1)
|
||||||
|
pack(out/'painting.bin',[image.resize((512,256),Image.Resampling.NEAREST)])
|
||||||
|
image.save(out/'painting-preview.png')
|
||||||
|
manifest['packs']['painting'] = ['SKY3','LAVA1','GSTONE1','BOSSA1','TROOA1','SARGA1']
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('archive', type=Path)
|
||||||
|
args = parser.parse_args()
|
||||||
|
data = args.archive.read_bytes()
|
||||||
|
assert hashlib.sha256(data).hexdigest() == SHA256, 'Freedoom archive hash mismatch'
|
||||||
|
archive = zipfile.ZipFile(io.BytesIO(data))
|
||||||
|
wad = Wad(archive.read(next(n for n in archive.namelist() if n.endswith('/freedoom2.wad'))))
|
||||||
|
out = ROOT / 'assets/hell'
|
||||||
|
out.mkdir(parents=True, exist_ok=True)
|
||||||
|
manifest = {'version': '0.13.0', 'url': URL, 'archive_sha256': SHA256, 'packs': {}}
|
||||||
|
monsters = {
|
||||||
|
'soldier': ['POSSA1','POSSB1','POSSC1','POSSD1','POSSE1','POSSF1','POSSG1','POSSH0','POSSI0','POSSJ0','POSSK0','POSSL0'],
|
||||||
|
'demon': ['SARGA1','SARGB1','SARGC1','SARGD1','SARGE1','SARGF1','SARGH1','SARGI0','SARGJ0','SARGK0','SARGL0','SARGN0'],
|
||||||
|
'imp': ['TROOA1','TROOB1','TROOC1','TROOD1','TROOE1','TROOF1','TROOH1','TROOI0','TROOJ0','TROOK0','TROOL0','TROOM0'],
|
||||||
|
'boss': ['BOSSA1','BOSSB1','BOSSC1','BOSSD1','BOSSE1','BOSSF1','BOSSH1','BOSSI0','BOSSJ0','BOSSK0','BOSSL0','BOSSO0'],
|
||||||
|
'pistol': ['PISGA0','PISGB0','PISGC0','PISGD0','PISGE0'],
|
||||||
|
'shotgun': ['SHTGA0','SHTGB0','SHTGC0','SHTGD0'],
|
||||||
|
'fire': ['BAL1A0','BAL1B0','BAL1C0','BAL1D0','BAL1E0'],
|
||||||
|
'portal': ['TFOGA0','TFOGB0','TFOGC0','TFOGD0','TFOGE0','TFOGF0','TFOGG0','TFOGH0'],
|
||||||
|
}
|
||||||
|
for name, names in monsters.items():
|
||||||
|
# Some Doom rotations are packed in mirrored pairs (A2A8 etc.).
|
||||||
|
for i, lump in enumerate(names):
|
||||||
|
if lump not in wad.lumps:
|
||||||
|
choices = [n for n in wad.lumps if n.startswith(lump[:5]) and len(n) in (6,8)]
|
||||||
|
if not choices:
|
||||||
|
raise KeyError(lump)
|
||||||
|
names[i] = sorted(choices)[0]
|
||||||
|
images = aligned(wad, names, (192, 160) if name in ('pistol','shotgun') else (128,128))
|
||||||
|
if name == 'portal':
|
||||||
|
# Recolour original Freedoom teleport animation to infernal orange/red.
|
||||||
|
for image in images:
|
||||||
|
image.putdata([(max(r,g), min(180,g//2), b//4, a)
|
||||||
|
for r,g,b,a in image.getdata()])
|
||||||
|
pack(out / (name+'.bin'), images)
|
||||||
|
manifest['packs'][name] = names
|
||||||
|
items = ['MEDIA0','ARM1A0','CLIPA0','SHELA0','SHOTA0','BKEYA0','BON2A0']
|
||||||
|
pack(out/'items.bin', [aligned(wad, [n])[0] for n in items])
|
||||||
|
manifest['packs']['items'] = items
|
||||||
|
textures = ['MARBLE1','GSTONE1','SKIN2','BIGDOOR2','SW1GARG','SP_HOT1']
|
||||||
|
flats = ['FLOOR7_2','F_SKY1','LAVA1','LAVA2','LAVA3','LAVA4']
|
||||||
|
images = [wad.texture(n).resize((128,128),Image.Resampling.NEAREST) for n in textures]
|
||||||
|
# F_SKY1 is a marker, use a real stone flat for ceiling.
|
||||||
|
flats[1] = 'CEIL5_2'
|
||||||
|
images += [wad.flat(n).resize((128,128),Image.Resampling.NEAREST) for n in flats]
|
||||||
|
pack(out/'materials.bin', images)
|
||||||
|
manifest['packs']['materials'] = textures+flats
|
||||||
|
interface_assets(wad,out,manifest)
|
||||||
|
for name in ['DSPISTOL','DSSHOTGN','DSSGCOCK','DSPLPAIN','DSPOPAIN','DSFIRSHT','DSPODTH1','DSITEMUP','DSDOROPN','DSTELEPT']:
|
||||||
|
raw = wad.lumps[name]
|
||||||
|
fmt, rate, count = struct.unpack_from('<HHI', raw)
|
||||||
|
assert fmt == 3 and count <= len(raw)-8
|
||||||
|
with wave.open(str(out/(name.lower()+'.wav')), 'wb') as wav:
|
||||||
|
wav.setparams((1,1,rate,0,'NONE','not compressed'))
|
||||||
|
wav.writeframes(raw[24:8+count-16])
|
||||||
|
for basename in ('COPYING','CREDITS'):
|
||||||
|
names = [n for n in archive.namelist() if Path(n).name.startswith(basename)]
|
||||||
|
if names:
|
||||||
|
(out/(basename+'.txt')).write_bytes(archive.read(names[0]))
|
||||||
|
else:
|
||||||
|
url = f'https://raw.githubusercontent.com/freedoom/freedoom/v0.13.0/{basename}.adoc'
|
||||||
|
(out/(basename+'.txt')).write_bytes(urllib.request.urlopen(url).read())
|
||||||
|
(out/'manifest.json').write_text(json.dumps(manifest,indent=2)+'\n',encoding='utf-8')
|
||||||
|
sheet = Image.new('RGBA',(6*128,3*128),(30,25,25,255))
|
||||||
|
for i, name in enumerate(monsters):
|
||||||
|
raw = (out/(name+'.bin')).read_bytes()
|
||||||
|
_,w,h,_,_ = struct.unpack_from('<5I',raw)
|
||||||
|
im = Image.frombytes('RGBA',(w,h),raw[20:20+w*h*4],'raw','BGRA')
|
||||||
|
im.thumbnail((128,128),Image.Resampling.NEAREST)
|
||||||
|
sheet.paste(im,((i%6)*128,(i//6)*128),im)
|
||||||
|
sheet.save(out/'preview.png')
|
||||||
|
print(f'Imported {len(manifest["packs"])} packs to {out}')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
96
tools/generators/make_hell_level.py
Normal file
96
tools/generators/make_hell_level.py
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
"""Compile the authored 32x32 hell map and validate progression offline."""
|
||||||
|
from pathlib import Path
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
grid = [[185 for _ in range(32)] for _ in range(32)]
|
||||||
|
|
||||||
|
|
||||||
|
def room(x0,y0,x1,y1):
|
||||||
|
for y in range(y0,y1+1):
|
||||||
|
for x in range(x0,x1+1):
|
||||||
|
grid[y][x] = 0
|
||||||
|
|
||||||
|
|
||||||
|
# Winding route through five arenas, with locked final sanctum.
|
||||||
|
room(2,23,10,29) # entry
|
||||||
|
room(3,14,10,21) # barracks
|
||||||
|
room(5,21,6,23)
|
||||||
|
room(12,13,21,22) # lava court
|
||||||
|
room(10,16,12,17)
|
||||||
|
room(13,3,21,10) # key crypt
|
||||||
|
room(17,10,18,13)
|
||||||
|
room(24,3,29,20) # final approach
|
||||||
|
room(21,6,24,7)
|
||||||
|
room(23,23,29,29) # boss
|
||||||
|
room(26,20,27,23)
|
||||||
|
# Pillars and cover; enough width for movement and projectile dodging.
|
||||||
|
for x,y in [(5,17),(8,18),(15,16),(19,19),(16,6),(19,7),(26,10),(27,16),(25,26)]:
|
||||||
|
grid[y][x] = 186
|
||||||
|
# Two discoverable secret doors.
|
||||||
|
room(2,7,8,11)
|
||||||
|
room(5,11,6,14)
|
||||||
|
room(24,23,25,23)
|
||||||
|
room(18,25,21,28)
|
||||||
|
room(21,26,23,27)
|
||||||
|
doors = [(5,12,0),(6,12,0),(22,26,1),(22,27,1),(22,6,2),(22,7,2)]
|
||||||
|
for x,y,_ in doors:
|
||||||
|
grid[y][x] = 188
|
||||||
|
for y in range(1,31):
|
||||||
|
for x in range(1,31):
|
||||||
|
if grid[y][x] == 185:
|
||||||
|
if x >= 23 and y >= 22:
|
||||||
|
grid[y][x] = 190 # infernal sanctum
|
||||||
|
elif x >= 23:
|
||||||
|
grid[y][x] = 187 # flesh-lined approach
|
||||||
|
elif x >= 12 and y <= 11:
|
||||||
|
grid[y][x] = 186 # crypt stone
|
||||||
|
|
||||||
|
# kind: 0 soldier, 1 melee, 2 imp, 3 boss. Coordinates at tile centres.
|
||||||
|
enemies = [(7,25,0),(8,28,0),(4,20,0),(8,16,1),(9,20,2),(4,15,0),
|
||||||
|
(14,20,2),(20,21,0),(20,14,2),(13,14,1),(18,17,1),(15,18,0),
|
||||||
|
(14,8,2),(20,4,2),(15,4,0),(20,9,1),(18,5,0),
|
||||||
|
(25,5,0),(28,8,2),(25,12,1),(28,15,0),(25,19,2),(28,19,1),
|
||||||
|
(24,25,2),(28,25,2),(27,28,3),(4,9,1),(7,8,0)]
|
||||||
|
# item kinds correspond to items.bin: health, armour, bullets, shells, shotgun, key, secret.
|
||||||
|
items = [(3,27,4),(4,28,3),(9,24,2),(3,19,0),(8,14,3),(9,15,2),
|
||||||
|
(13,21,1),(14,14,3),(20,20,0),(19,14,2),(14,4,0),(20,3,5),
|
||||||
|
(17,9,3),(25,4,2),(28,11,0),(25,17,3),(28,20,3),
|
||||||
|
(24,24,0),(29,28,3),(24,28,2),(3,8,1),(7,10,3),(19,26,0),(20,27,3)]
|
||||||
|
for x,y,_ in enemies+items:
|
||||||
|
assert grid[y][x] == 0, (x,y)
|
||||||
|
|
||||||
|
|
||||||
|
def reachable(open_groups=()):
|
||||||
|
seen = {(3,26)}
|
||||||
|
todo = deque(seen)
|
||||||
|
while todo:
|
||||||
|
x,y = todo.popleft()
|
||||||
|
for nx,ny in [(x-1,y),(x+1,y),(x,y-1),(x,y+1)]:
|
||||||
|
if (nx,ny) in seen or not (0<=nx<32 and 0<=ny<32):
|
||||||
|
continue
|
||||||
|
if grid[ny][nx] and (nx,ny) not in [(x,y) for x,y,group in doors if group in open_groups]:
|
||||||
|
continue
|
||||||
|
seen.add((nx,ny))
|
||||||
|
todo.append((nx,ny))
|
||||||
|
return seen
|
||||||
|
|
||||||
|
|
||||||
|
assert (20,3) in reachable() and (27,28) not in reachable()
|
||||||
|
assert (27,28) not in reachable((0,1)), 'Secrets must not bypass the key gate'
|
||||||
|
assert (27,28) in reachable((2,)), 'Secrets must remain optional'
|
||||||
|
assert all((x,y) in reachable((0,1,2)) for x,y,_ in enemies+items)
|
||||||
|
# Conservative ammunition budget: half the pellets land at minimum damage.
|
||||||
|
bullets = 80 + 40*sum(kind == 2 for _,_,kind in items)
|
||||||
|
shells = 16 + 12*sum(kind == 3 for _,_,kind in items)
|
||||||
|
enemy_hp = sum((55,105,85,1100)[kind] for _,_,kind in enemies)
|
||||||
|
assert (bullets + shells*7)*8//2 > enemy_hp
|
||||||
|
lines = ['; Generated by tools/generators/make_hell_level.py.','.data','hfMapTemplate label byte']
|
||||||
|
lines += [' db '+','.join(map(str,row)) for row in grid]
|
||||||
|
lines += ['hfEnemySeeds label dword']
|
||||||
|
lines += [f' dd {x}*FP_1+FP_HALF,{y}*FP_1+FP_HALF,{kind}' for x,y,kind in enemies]
|
||||||
|
lines += [f'HF_ENEMIES equ {len(enemies)}','hfItemSeeds label dword']
|
||||||
|
lines += [f' dd {x}*FP_1+FP_HALF,{y}*FP_1+FP_HALF,{kind}' for x,y,kind in items]
|
||||||
|
lines += [f'HF_ITEMS equ {len(items)}','.code']
|
||||||
|
(ROOT/'src_graphics/hell_fps_map.inc').write_text('\n'.join(lines)+'\n',encoding='ascii')
|
||||||
|
print(f'Hell map: {len(enemies)} enemies, {len(items)} pickups, key gate and two secrets validated')
|
||||||
53
tools/tests/check_hell_fps.py
Normal file
53
tools/tests/check_hell_fps.py
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
"""Build and execute native MASM combat, renderer, progression and save checks."""
|
||||||
|
from pathlib import Path
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import subprocess
|
||||||
|
from PIL import Image
|
||||||
|
from check_artifacts import toolchain
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
OUT = ROOT / 'build/scratch'
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--window',action='store_true')
|
||||||
|
options = parser.parse_args()
|
||||||
|
OUT.mkdir(parents=True, exist_ok=True)
|
||||||
|
before = {p: hashlib.sha256(p.read_bytes()).digest() for p in (ROOT/'runtime').rglob('*') if p.is_file()}
|
||||||
|
ml, link = toolchain()
|
||||||
|
sdk = Path('C:/Program Files (x86)/Windows Kits/10/Lib')
|
||||||
|
lib = next(p/'um/x86' for p in sorted(sdk.iterdir(), reverse=True)
|
||||||
|
if (p/'um/x86/kernel32.lib').exists())
|
||||||
|
commands = [[ml,'/c','/coff','/Cp','/nologo','/I.','/DHELL_FPS_TEST=1',
|
||||||
|
'/Fobuild/scratch/hell-fps.obj','poker.asm'],
|
||||||
|
[link,'/nologo','/SUBSYSTEM:CONSOLE','/LIBPATH:'+str(lib),
|
||||||
|
'/OUT:build/scratch/hell-fps-test.exe','build/scratch/hell-fps.obj',
|
||||||
|
'kernel32.lib','winmm.lib','user32.lib','ws2_32.lib','gdi32.lib']]
|
||||||
|
if options.window:
|
||||||
|
commands[0].insert(1,'/DHELL_FPS_WINDOW_TEST=1')
|
||||||
|
for command in commands:
|
||||||
|
subprocess.run(list(map(str,command)),cwd=ROOT,check=True,timeout=120)
|
||||||
|
try:
|
||||||
|
result = subprocess.run([str(OUT/'hell-fps-test.exe')],cwd=OUT if options.window else ROOT,timeout=90)
|
||||||
|
finally:
|
||||||
|
assert before == {p: hashlib.sha256(p.read_bytes()).digest() for p in (ROOT/'runtime').rglob('*') if p.is_file()}, 'Test changed player saves'
|
||||||
|
assert result.returncode == 0, f'Native test failed at stage/exception {result.returncode:#x}'
|
||||||
|
for name in (('window',) if options.window else ('entry','court','portal','victory')):
|
||||||
|
raw = (OUT/f'hell-{name}.bgra').read_bytes()
|
||||||
|
assert len(raw) == 640*480*4
|
||||||
|
image = Image.frombytes('RGBA',(640,480),raw,'raw','BGRA').convert('RGB')
|
||||||
|
image.save(OUT/f'hell-{name}.png')
|
||||||
|
assert len(image.getcolors(640*480)) > 100, f'{name}: blank renderer'
|
||||||
|
if options.window:
|
||||||
|
print('PASS: real Win32 window, 120 presented frames, mouse-down/up events, firing, Mick Gordon playback, six SFX channels, clean return')
|
||||||
|
return
|
||||||
|
print('PASS: painting entrance, collision/LOS, multi-target seven-pellet shotgun, '
|
||||||
|
'single-ray pistol, armour/death/retry, gates/secrets, '
|
||||||
|
'1050 AI ticks, finite-ammo boss fight with dodging, first-clear reward, '
|
||||||
|
'native/preview save roundtrips, return, 4 native render captures')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
509
tools/tests/hell_fps_test.inc
Normal file
509
tools/tests/hell_fps_test.inc
Normal file
|
|
@ -0,0 +1,509 @@
|
||||||
|
.data
|
||||||
|
hfTestStage dd 1
|
||||||
|
hfTestIo dd 0
|
||||||
|
hfTestFrame db 'build\scratch\hell-entry.bgra',0
|
||||||
|
hfTestCourt db 'build\scratch\hell-court.bgra',0
|
||||||
|
hfTestHall db 'build\scratch\hell-portal.bgra',0
|
||||||
|
hfTestWin db 'build\scratch\hell-victory.bgra',0
|
||||||
|
hfTestPreview db 'build\scratch\hf-prev.dat',0
|
||||||
|
hfTestProfileDir db 'build\scratch',0
|
||||||
|
hfTestProfileName db 'hell-profile.dat',0
|
||||||
|
hfTestBoss dd 0
|
||||||
|
hfTestStrafe dd 5000
|
||||||
|
.code
|
||||||
|
HFTestAimBoss PROC uses esi edi ebx
|
||||||
|
local vx:DWORD,vy:DWORD,best:DWORD
|
||||||
|
mov esi,hfTestBoss
|
||||||
|
mov eax,[esi].HFEnemy.x
|
||||||
|
sub eax,playerX
|
||||||
|
sar eax,8
|
||||||
|
mov vx,eax
|
||||||
|
mov eax,[esi].HFEnemy.y
|
||||||
|
sub eax,playerY
|
||||||
|
sar eax,8
|
||||||
|
mov vy,eax
|
||||||
|
mov best,80000000h
|
||||||
|
xor edi,edi
|
||||||
|
hf_test_aim_angle:
|
||||||
|
mov eax,tableCos[edi*4]
|
||||||
|
sar eax,8
|
||||||
|
imul eax,vx
|
||||||
|
mov ebx,eax
|
||||||
|
mov eax,tableSin[edi*4]
|
||||||
|
sar eax,8
|
||||||
|
imul eax,vy
|
||||||
|
add eax,ebx
|
||||||
|
cmp eax,best
|
||||||
|
jle @F
|
||||||
|
mov best,eax
|
||||||
|
mov playerAngle,edi
|
||||||
|
@@:
|
||||||
|
inc edi
|
||||||
|
cmp edi,512
|
||||||
|
jb hf_test_aim_angle
|
||||||
|
ret
|
||||||
|
HFTestAimBoss ENDP
|
||||||
|
|
||||||
|
HFTestCapture PROC uses ebx path:DWORD
|
||||||
|
invoke CreateFileA,path,40000000h,0,0,2,80h,0
|
||||||
|
mov ebx,eax
|
||||||
|
invoke WriteFile,ebx,OFFSET screenBuffer,SCREEN_SIZE*4,OFFSET hfTestIo,0
|
||||||
|
invoke CloseHandle,ebx
|
||||||
|
ret
|
||||||
|
HFTestCapture ENDP
|
||||||
|
|
||||||
|
RunHellFPSTest PROC uses esi edi ebx
|
||||||
|
call InitHall3D
|
||||||
|
call LoadTextures3D
|
||||||
|
call LoadHallAssets3D
|
||||||
|
call InitializeCasinoSprites3D
|
||||||
|
mov hfTestStage,2
|
||||||
|
call HFLoad
|
||||||
|
test eax,eax
|
||||||
|
jz hf_test_fail
|
||||||
|
mov playerX,HF_PORTAL_X
|
||||||
|
mov playerY,29*FP_1
|
||||||
|
mov playerAngle,128
|
||||||
|
mov engineMode,MODE_WALK
|
||||||
|
call UpdateCamera3D
|
||||||
|
call CheckHallInteractions3D
|
||||||
|
; Approach must be within the standard interaction distance.
|
||||||
|
mov playerY,FP_1*294/10
|
||||||
|
call CheckHallInteractions3D
|
||||||
|
cmp interactTargetId,INTERACT_HELL_FPS
|
||||||
|
jne hf_test_fail
|
||||||
|
invoke CanWalk3D,HF_PORTAL_X,FP_1*294/10
|
||||||
|
cmp eax,1
|
||||||
|
jne hf_test_fail
|
||||||
|
; Old standing frame no longer blocks the west aisle.
|
||||||
|
invoke CanWalk3D,FP_1*25/10,11*FP_1
|
||||||
|
cmp eax,1
|
||||||
|
jne hf_test_fail
|
||||||
|
mov playerX,4*FP_1
|
||||||
|
mov playerY,12*FP_1
|
||||||
|
mov playerAngle,256
|
||||||
|
call UpdateCamera3D
|
||||||
|
mov interactTargetId,0
|
||||||
|
call CheckHallInteractions3D
|
||||||
|
cmp interactTargetId,INTERACT_HELL_FPS
|
||||||
|
je hf_test_fail
|
||||||
|
mov playerX,HF_PORTAL_X
|
||||||
|
mov playerY,FP_1*255/10
|
||||||
|
mov playerAngle,128
|
||||||
|
call UpdateCamera3D
|
||||||
|
call RenderCeilingAndFloor
|
||||||
|
call RenderWalls
|
||||||
|
call PrepareCityDepth3D
|
||||||
|
call RenderHall3D
|
||||||
|
invoke HFTestCapture,OFFSET hfTestHall
|
||||||
|
mov hfOldX,HF_PORTAL_X
|
||||||
|
mov hfOldY,FP_1*294/10
|
||||||
|
mov hfOldAngle,128
|
||||||
|
mov hfOldMap,OFFSET casinoMap
|
||||||
|
mov hfOldFloor,100
|
||||||
|
mov hfOldLevel,0
|
||||||
|
mov hfOldEye,HALL_STANDING_EYE
|
||||||
|
mov hfOldCeiling,FP_1*5/2
|
||||||
|
call HFReset
|
||||||
|
mov hfTestStage,3
|
||||||
|
call RenderHellFPS3D
|
||||||
|
invoke HFTestCapture,OFFSET hfTestFrame
|
||||||
|
invoke HFLineClear,3*FP_1+FP_HALF,26*FP_1+FP_HALF,7*FP_1+FP_HALF,26*FP_1+FP_HALF
|
||||||
|
cmp eax,1
|
||||||
|
jne hf_test_fail
|
||||||
|
invoke HFLineClear,3*FP_1+FP_HALF,26*FP_1+FP_HALF,13*FP_1,26*FP_1+FP_HALF
|
||||||
|
test eax,eax
|
||||||
|
jnz hf_test_fail
|
||||||
|
invoke CanWalk3D,0,0
|
||||||
|
test eax,eax
|
||||||
|
jnz hf_test_fail
|
||||||
|
mov hfTestStage,4
|
||||||
|
; Same hitscan must stop at wall, then damage the nearest target in open space.
|
||||||
|
mov hfEnemies.x,12*FP_1
|
||||||
|
mov hfEnemies.y,26*FP_1+FP_HALF
|
||||||
|
mov hfEnemies.hp,100
|
||||||
|
mov hfEnemies[SIZEOF HFEnemy].hp,0
|
||||||
|
call HFFire
|
||||||
|
cmp hfEnemies.hp,100
|
||||||
|
jne hf_test_fail
|
||||||
|
mov hfEnemies.x,7*FP_1
|
||||||
|
mov hfCooldown,0
|
||||||
|
call HFFire
|
||||||
|
cmp hfEnemies.hp,100
|
||||||
|
jae hf_test_fail
|
||||||
|
cmp hfBullets,78
|
||||||
|
jne hf_test_fail
|
||||||
|
mov hfTestStage,5
|
||||||
|
mov bankroll,12345
|
||||||
|
invoke HFPlayerCanWalk,hfEnemies.x,hfEnemies.y
|
||||||
|
test eax,eax
|
||||||
|
jnz hf_test_fail
|
||||||
|
mov hfArmor,20
|
||||||
|
invoke HFDamagePlayer,10
|
||||||
|
cmp hfHealth,95
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfArmor,15
|
||||||
|
jne hf_test_fail
|
||||||
|
mov hfArmor,0
|
||||||
|
invoke HFDamagePlayer,1000
|
||||||
|
cmp hfState,1
|
||||||
|
jne hf_test_fail
|
||||||
|
call HFReset
|
||||||
|
cmp hfState,0
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfHealth,100
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp bankroll,12345
|
||||||
|
jne hf_test_fail
|
||||||
|
; Natural shotgun pickup and consumption; no injected weapon ownership.
|
||||||
|
mov playerX,3*FP_1+FP_HALF
|
||||||
|
mov playerY,27*FP_1+FP_HALF
|
||||||
|
call HFPickups
|
||||||
|
cmp hfHasShotgun,1
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfShells,16
|
||||||
|
jne hf_test_fail
|
||||||
|
call HFFire
|
||||||
|
cmp hfShells,15
|
||||||
|
jne hf_test_fail
|
||||||
|
mov hfTestStage,12
|
||||||
|
; Three separated targets: pistol hits centre only; one shell hits all three.
|
||||||
|
call HFReset
|
||||||
|
mov esi,OFFSET hfEnemies
|
||||||
|
mov ecx,HF_ENEMIES
|
||||||
|
@@:
|
||||||
|
mov [esi].HFEnemy.hp,0
|
||||||
|
add esi,SIZEOF HFEnemy
|
||||||
|
loop @B
|
||||||
|
mov hfEnemies.x,8*FP_1
|
||||||
|
mov hfEnemies.y,FP_1*103/4
|
||||||
|
mov hfEnemies.hp,1000
|
||||||
|
mov hfEnemies[SIZEOF HFEnemy].x,8*FP_1
|
||||||
|
mov hfEnemies[SIZEOF HFEnemy].y,FP_1*106/4
|
||||||
|
mov hfEnemies[SIZEOF HFEnemy].hp,1000
|
||||||
|
mov hfEnemies[2*SIZEOF HFEnemy].x,8*FP_1
|
||||||
|
mov hfEnemies[2*SIZEOF HFEnemy].y,FP_1*109/4
|
||||||
|
mov hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
||||||
|
call HFFire
|
||||||
|
cmp hfEnemies.hp,1000
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfEnemies[SIZEOF HFEnemy].hp,1000
|
||||||
|
jae hf_test_fail
|
||||||
|
cmp hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
||||||
|
jne hf_test_fail
|
||||||
|
mov hfCooldown,0
|
||||||
|
mov hfWeapon,1
|
||||||
|
mov hfShells,2
|
||||||
|
call HFFire
|
||||||
|
cmp hfShells,1
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfEnemies.hp,1000
|
||||||
|
jae hf_test_fail
|
||||||
|
cmp hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
||||||
|
jae hf_test_fail
|
||||||
|
mov esi,OFFSET hfImpacts
|
||||||
|
mov ecx,HF_SHOT_PELLETS
|
||||||
|
@@:
|
||||||
|
cmp [esi].HFImpact.life,8
|
||||||
|
jne hf_test_fail
|
||||||
|
add esi,SIZEOF HFImpact
|
||||||
|
loop @B
|
||||||
|
; The whole pellet cone must stop at the separating wall.
|
||||||
|
mov hfEnemies.x,12*FP_1
|
||||||
|
mov hfEnemies.hp,1000
|
||||||
|
mov hfEnemies[SIZEOF HFEnemy].x,12*FP_1
|
||||||
|
mov hfEnemies[SIZEOF HFEnemy].hp,1000
|
||||||
|
mov hfEnemies[2*SIZEOF HFEnemy].x,12*FP_1
|
||||||
|
mov hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
||||||
|
mov hfCooldown,0
|
||||||
|
call HFFire
|
||||||
|
cmp hfEnemies.hp,1000
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfEnemies[SIZEOF HFEnemy].hp,1000
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfEnemies[2*SIZEOF HFEnemy].hp,1000
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfShells,0
|
||||||
|
jne hf_test_fail
|
||||||
|
mov hfCooldown,0
|
||||||
|
call HFFire
|
||||||
|
cmp hfCooldown,0
|
||||||
|
jne hf_test_fail
|
||||||
|
call HFReset
|
||||||
|
mov hfTestStage,6
|
||||||
|
mov playerX,21*FP_1+FP_HALF
|
||||||
|
mov playerY,6*FP_1+FP_HALF
|
||||||
|
call HFUse
|
||||||
|
cmp hfMap[6*32+22],188
|
||||||
|
jne hf_test_fail
|
||||||
|
mov playerX,20*FP_1+FP_HALF
|
||||||
|
mov playerY,3*FP_1+FP_HALF
|
||||||
|
call HFPickups
|
||||||
|
cmp hfKey,1
|
||||||
|
jne hf_test_fail
|
||||||
|
mov playerX,21*FP_1+FP_HALF
|
||||||
|
mov playerY,6*FP_1+FP_HALF
|
||||||
|
call HFUse
|
||||||
|
cmp hfMap[6*32+22],0
|
||||||
|
jne hf_test_fail
|
||||||
|
mov playerX,5*FP_1+FP_HALF
|
||||||
|
mov playerY,13*FP_1+FP_HALF
|
||||||
|
call HFUse
|
||||||
|
call HFUse
|
||||||
|
cmp hfSecrets,1
|
||||||
|
jne hf_test_fail
|
||||||
|
mov playerX,23*FP_1
|
||||||
|
mov playerY,27*FP_1
|
||||||
|
call HFUse
|
||||||
|
cmp hfSecrets,2
|
||||||
|
jne hf_test_fail
|
||||||
|
mov hfTestStage,7
|
||||||
|
; Real AI/projectile/pickup loop stress; no alternate Python simulation.
|
||||||
|
call HFReset
|
||||||
|
mov playerX,14*FP_1
|
||||||
|
mov playerY,21*FP_1
|
||||||
|
mov playerAngle,430
|
||||||
|
mov hfWeapon,1
|
||||||
|
mov hfHasShotgun,1
|
||||||
|
mov hfShells,80
|
||||||
|
mov hfFireHeld,1
|
||||||
|
mov ebx,1050
|
||||||
|
hf_test_combat:
|
||||||
|
mov hfHealth,100
|
||||||
|
mov hfState,0
|
||||||
|
call HFTick
|
||||||
|
dec ebx
|
||||||
|
jnz hf_test_combat
|
||||||
|
mov hfFireHeld,0
|
||||||
|
call RenderHellFPS3D
|
||||||
|
invoke HFTestCapture,OFFSET hfTestCourt
|
||||||
|
mov hfTestStage,11
|
||||||
|
; Isolated boss encounter: real pickups, finite ammo, normal damage and ticks.
|
||||||
|
call HFReset
|
||||||
|
mov esi,OFFSET hfEnemies
|
||||||
|
mov ecx,HF_ENEMIES
|
||||||
|
hf_test_isolate_boss:
|
||||||
|
cmp [esi].HFEnemy.kind,3
|
||||||
|
jne hf_test_disable_enemy
|
||||||
|
mov hfTestBoss,esi
|
||||||
|
jmp @F
|
||||||
|
hf_test_disable_enemy:
|
||||||
|
mov [esi].HFEnemy.hp,0
|
||||||
|
@@:
|
||||||
|
add esi,SIZEOF HFEnemy
|
||||||
|
loop hf_test_isolate_boss
|
||||||
|
mov playerX,3*FP_1+FP_HALF
|
||||||
|
mov playerY,27*FP_1+FP_HALF
|
||||||
|
call HFPickups
|
||||||
|
mov playerX,4*FP_1+FP_HALF
|
||||||
|
mov playerY,28*FP_1+FP_HALF
|
||||||
|
call HFPickups
|
||||||
|
mov playerX,8*FP_1+FP_HALF
|
||||||
|
mov playerY,14*FP_1+FP_HALF
|
||||||
|
call HFPickups
|
||||||
|
mov playerX,27*FP_1+FP_HALF
|
||||||
|
mov playerY,24*FP_1+FP_HALF
|
||||||
|
mov playerAngle,128
|
||||||
|
mov hfFireHeld,1
|
||||||
|
mov ebx,1400
|
||||||
|
hf_test_boss_fight:
|
||||||
|
cmp playerX,28*FP_1+FP_HALF
|
||||||
|
jl @F
|
||||||
|
mov hfTestStrafe,-5000
|
||||||
|
@@:
|
||||||
|
cmp playerX,26*FP_1+FP_HALF
|
||||||
|
jg @F
|
||||||
|
mov hfTestStrafe,5000
|
||||||
|
@@:
|
||||||
|
mov eax,playerX
|
||||||
|
add eax,hfTestStrafe
|
||||||
|
push eax
|
||||||
|
invoke HFPlayerCanWalk,eax,playerY
|
||||||
|
pop ecx
|
||||||
|
test eax,eax
|
||||||
|
jz @F
|
||||||
|
mov playerX,ecx
|
||||||
|
@@:
|
||||||
|
call HFTestAimBoss
|
||||||
|
call HFTick
|
||||||
|
cmp hfState,0
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hfKills,1
|
||||||
|
je hf_test_boss_killed
|
||||||
|
dec ebx
|
||||||
|
jnz hf_test_boss_fight
|
||||||
|
jmp hf_test_fail
|
||||||
|
hf_test_boss_killed:
|
||||||
|
mov hfFireHeld,0
|
||||||
|
mov hfTestStage,8
|
||||||
|
; Locked exit, then one-time reward, then repeat use cannot pay twice.
|
||||||
|
call HFReset
|
||||||
|
mov bankroll,10000
|
||||||
|
mov hfCompleted,0
|
||||||
|
mov playerX,28*FP_1
|
||||||
|
mov playerY,28*FP_1
|
||||||
|
call HFUse
|
||||||
|
cmp hfState,0
|
||||||
|
jne hf_test_fail
|
||||||
|
mov esi,OFFSET hfEnemies
|
||||||
|
mov ecx,HF_ENEMIES
|
||||||
|
@@:
|
||||||
|
mov [esi].HFEnemy.hp,0
|
||||||
|
add esi,SIZEOF HFEnemy
|
||||||
|
loop @B
|
||||||
|
call HFUse
|
||||||
|
cmp hfState,2
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp bankroll,16660
|
||||||
|
jne hf_test_fail
|
||||||
|
call HFUse
|
||||||
|
cmp bankroll,16660
|
||||||
|
jne hf_test_fail
|
||||||
|
mov playerX,26*FP_1
|
||||||
|
mov playerY,24*FP_1
|
||||||
|
mov playerAngle,96
|
||||||
|
mov hfTicks,18000
|
||||||
|
mov hfKills,28
|
||||||
|
mov hfSecrets,2
|
||||||
|
call RenderHellFPS3D
|
||||||
|
invoke HFTestCapture,OFFSET hfTestWin
|
||||||
|
mov hfTestStage,9
|
||||||
|
; Save extension survives native signed profile round trip.
|
||||||
|
mov nativeProfilesDirPtr3D,OFFSET hfTestProfileDir
|
||||||
|
mov nativeProfileActive3D,1
|
||||||
|
invoke CopyCString,OFFSET saveFileName,OFFSET hfTestProfileName
|
||||||
|
call BuildNativeProfileDir3D
|
||||||
|
call SaveGame
|
||||||
|
mov hfCompleted,0
|
||||||
|
call LoadSave
|
||||||
|
cmp hfCompleted,1
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp bankroll,16660
|
||||||
|
jne hf_test_fail
|
||||||
|
mov hfTestStage,10
|
||||||
|
; Preview save uses the same new bit without changing its 40-byte format.
|
||||||
|
mov nativeProfileActive3D,0
|
||||||
|
mov arrivalConfirmed3D,1
|
||||||
|
invoke CopyCString,OFFSET savePath3D,OFFSET hfTestPreview
|
||||||
|
call SaveProgress3D
|
||||||
|
mov hfCompleted,0
|
||||||
|
call LoadProgress3D
|
||||||
|
cmp hfCompleted,1
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp bankroll,16660
|
||||||
|
jne hf_test_fail
|
||||||
|
mov arrivalConfirmed3D,0
|
||||||
|
call LeaveHellFPS3D
|
||||||
|
cmp engineMode,MODE_WALK
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp playerX,HF_PORTAL_X
|
||||||
|
jne hf_test_fail
|
||||||
|
cmp hallLevel3D,0
|
||||||
|
jne hf_test_fail
|
||||||
|
call FreeHellFPS3D
|
||||||
|
xor eax,eax
|
||||||
|
ret
|
||||||
|
hf_test_fail:
|
||||||
|
mov eax,hfTestStage
|
||||||
|
ret
|
||||||
|
RunHellFPSTest ENDP
|
||||||
|
|
||||||
|
IFDEF HELL_FPS_WINDOW_TEST
|
||||||
|
.data
|
||||||
|
hfWindowShot db 'build\scratch\hell-window.bgra',0
|
||||||
|
hfMusicStatus db 'status hellmusic mode',0
|
||||||
|
hfAudioMode db 32 dup(0)
|
||||||
|
.code
|
||||||
|
RunHellFPSWindowTest PROC uses esi ebx
|
||||||
|
call InitPaths3D
|
||||||
|
call Create3DWindow
|
||||||
|
test eax,eax
|
||||||
|
jz hf_window_failed
|
||||||
|
; Keep test window modest and avoid taking keyboard/mouse focus.
|
||||||
|
cmp fullscreen3D,0
|
||||||
|
je @F
|
||||||
|
call ToggleFullscreen3D
|
||||||
|
@@:
|
||||||
|
invoke SetWindowPos,h3DWnd,0,80,80,800,640,14h
|
||||||
|
call InitHall3D
|
||||||
|
call LoadTextures3D
|
||||||
|
call LoadHallAssets3D
|
||||||
|
call InitializeCasinoSprites3D
|
||||||
|
mov playerX,HF_PORTAL_X
|
||||||
|
mov playerY,FP_1*294/10
|
||||||
|
mov playerAngle,128
|
||||||
|
mov engineMode,MODE_WALK
|
||||||
|
call BeginHellFPS3D
|
||||||
|
cmp engineMode,MODE_HELL_FPS
|
||||||
|
jne hf_window_failed
|
||||||
|
cmp hfMusicError,0
|
||||||
|
jne hf_window_failed
|
||||||
|
cmp hfSoundFailures,0
|
||||||
|
jne hf_window_failed
|
||||||
|
cmp hfVolumeError,0
|
||||||
|
jne hf_window_failed
|
||||||
|
mov settingsVolume,10
|
||||||
|
call HFUpdateMusicVolume
|
||||||
|
cmp hfVolumeError,0
|
||||||
|
jne hf_window_failed
|
||||||
|
mov eax,SIZEOF hfVolumePrefix-1
|
||||||
|
cmp DWORD PTR hfVolumeCommand[eax],00303533h ; "350", not full volume 1000
|
||||||
|
jne hf_window_failed
|
||||||
|
mov settingsVolume,0
|
||||||
|
call HFUpdateMusicVolume
|
||||||
|
cmp hfVolumeError,0
|
||||||
|
jne hf_window_failed
|
||||||
|
mov eax,SIZEOF hfVolumePrefix-1
|
||||||
|
cmp WORD PTR hfVolumeCommand[eax],0030h
|
||||||
|
jne hf_window_failed
|
||||||
|
mov settingsVolume,5
|
||||||
|
call HFUpdateMusicVolume
|
||||||
|
cmp hfVolumeError,0
|
||||||
|
jne hf_window_failed
|
||||||
|
invoke ShowWindow,h3DWnd,4
|
||||||
|
mov esi,120
|
||||||
|
hf_window_frame:
|
||||||
|
invoke PeekMessageA,OFFSET msg3D,0,0,0,PM_REMOVE
|
||||||
|
test eax,eax
|
||||||
|
jz hf_window_simulate
|
||||||
|
invoke TranslateMessage,OFFSET msg3D
|
||||||
|
invoke DispatchMessageA,OFFSET msg3D
|
||||||
|
jmp hf_window_frame
|
||||||
|
hf_window_simulate:
|
||||||
|
mov deltaTime3D,17
|
||||||
|
add animationClock3D,17
|
||||||
|
cmp esi,110
|
||||||
|
jne @F
|
||||||
|
invoke WindowEvents3D,h3DWnd,201h,1,0
|
||||||
|
@@:
|
||||||
|
cmp esi,35
|
||||||
|
jne @F
|
||||||
|
invoke WindowEvents3D,h3DWnd,202h,0,0
|
||||||
|
@@:
|
||||||
|
call HandleHellFPS3D
|
||||||
|
call RenderHellFPS3D
|
||||||
|
call Present3D
|
||||||
|
invoke Sleep,17
|
||||||
|
dec esi
|
||||||
|
jnz hf_window_frame
|
||||||
|
invoke HFTestCapture,OFFSET hfWindowShot
|
||||||
|
cmp hfBullets,80
|
||||||
|
jae hf_window_failed
|
||||||
|
cmp hfFireHeld,0
|
||||||
|
jne hf_window_failed
|
||||||
|
invoke mciSendStringA,OFFSET hfMusicStatus,OFFSET hfAudioMode,32,0
|
||||||
|
test eax,eax
|
||||||
|
jnz hf_window_failed
|
||||||
|
cmp DWORD PTR hfAudioMode,'yalp'
|
||||||
|
jne hf_window_failed
|
||||||
|
call LeaveHellFPS3D
|
||||||
|
call FreeHellFPS3D
|
||||||
|
invoke DestroyWindow,h3DWnd
|
||||||
|
xor eax,eax
|
||||||
|
ret
|
||||||
|
hf_window_failed:
|
||||||
|
call FreeHellFPS3D
|
||||||
|
mov eax,111
|
||||||
|
ret
|
||||||
|
RunHellFPSWindowTest ENDP
|
||||||
|
ENDIF
|
||||||
Loading…
Add table
Reference in a new issue