Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Remilia Scarlet

Members
  • Content count

    4264
  • Joined

  • Last visited

Status Updates posted by Remilia Scarlet

  1. "May the 4th be wi-"

     

    qkyn3rgnqhvy.jpg

    1. Liberation

      Liberation

      For the Emperor!

  2. *needs to remove unused textures from a pk3 file*

     

    *Opens up Emacs*

     

    *Starts SLIME, loads up her Waddle library*

     

    *Types up some Common Lisp REPL fun*

    (let ((textures (check-for-unused-textures-in-wads
                     "/home/alexa/storage/bin/games/doom/Mine/Heretic Map/corax.pk3"
    
                     '("textures/redsky1.png" "textures/underwater.png"
                       "textures/q1hrtex/cop1_2.png" "textures/QFLAT07.png"
                       "textures/adel_v80.png")
    
                     #P"/home/alexa/storage/bin/games/doom/Mine/Heretic Map/corax-start.wad"
                     #P"/home/alexa/storage/bin/games/doom/Mine/Heretic Map/corax01.wad"
                     #P"/home/alexa/storage/bin/games/doom/Mine/Heretic Map/corax02.wad"
                     #P"/home/alexa/storage/bin/games/doom/Mine/Heretic Map/corax03.wad"
                     #P"/home/alexa/storage/bin/games/doom/Mine/Heretic Map/corax04.wad"
                     #P"/home/alexa/storage/bin/games/doom/Mine/Heretic Map/corax31.wad")))
    
      (format t "~%** Found ~a unused textures~%" (length textures))
    
      (format t "Writing textures to output file and modifying pk3~%")
      (with-open-file (out #P"/mnt/storage/alexa-extended/bin/games/doom/Mine/Heretic Map/unused-textures.txt"
                           :direction :output
                           :if-exists :supersede :if-does-not-exist :create)
        (loop for tex across textures
           do (progn
                (write-line tex out)
                (sb-ext:run-program
                 "zip" (list "-d" "/mnt/storage/alexa-extended/bin/games/doom/Mine/Heretic Map/corax.pk3" tex)
                 :search t :wait t
                 :output *standard-output*)))))
    

     

    And that's how I optimized SoTNR's pk3 in just a few minutes :D  I really need to wrap that in a command line program someday.

     

    Speaking of which, I've gotten a lot of things in the past few days, so SoTNR is now 95-98% done.  All that's left is:

    • More playtesting and last-minute tweaking
    • Text file proofreading
    • Music tweaks
    • Tweak some brightmaps
    1. Show previous comments  5 more
    2. DoctorGenesis

      DoctorGenesis

      What's SLIME?

    3. Remilia Scarlet

      Remilia Scarlet

      "Superior Lisp Interaction Mode or Emacs".  It essentially turns Emacs into a Common Lisp IDE on steroids. 

    4. DoctorGenesis

      DoctorGenesis

      @YukiRaven Huh, that's rather interesting.

  3. Made this for dinner tonight, but I wasn't paying attention and added way too much pork.

     

    So... ants harvesting a dense forest of vines?

    1. Show previous comments  3 more
    2. Albertoni

      Albertoni

      The language is easy to abuse to make some simple silly poetry, so they obviously do that.

       

      There's a Brazilian dish called "vaca atolada", "cow stuck in mud" that's almost the same thing, but with cow and cassava instead of pork and noodles. When done right, it does turn into a thick cream.

      The story for that one is that when a cow did get stuck, you couldn't get to the lower meats, so they just killed it, took off the rib meat and that was it. It's traditionally done with rib meat, but eh. Traditions are made to be improved. :P

    3. rdwpa

      rdwpa

      That reminds me: I want to try century eggs one day. I've never heard of 'ants climbing up a tree', but now I'm tempted to look up some 'interestingly' named foods. 

    4. GarrettChan

      GarrettChan

      @rdwpa Oh, I remember that this thing was said to be one of the most disgusting foods in the world? Lol. It's funny that the English name sounds like an expired egg or something like that. Besides eating it directly, one classic is porridge with this and water-cooked pork slice (don't know whether this is described correctly...).

    1. esselfortium

      esselfortium

      I notice there doesn't seem to be a date confirmed for either of them, though, so I don't think this is really anything we didn't already know...

    2. MrGlide

      MrGlide

      you see kong yet? I highly advise it, it was really really really good.

    3. Remilia Scarlet

      Remilia Scarlet

      @esselfortium well I hadn't heard it yet, so it was something I didn't know ^_^

       

      @MrGlide Not yet, but I've heard it's good.  I did see the post-credits scene, however, and uhh... yeah I lost my shit.

  4. tlQfTda.png

     

    This is about 5 days worth of work.  I think I've gotten faster.

    1. DMPhobos

      DMPhobos

      It would take at least 3 full weeks of work for me to make something this size and with half the details
      Really impressive!

    2. riderr3

      riderr3

      Yeah. Pretty well done.

      Here what I've done for 2 weeks:
      hyRGqDK.jpg

      Some days earlier:

      Spoiler


      oaG1Itd.jpg

      I leaving detailing for later.

    3. Remilia Scarlet

      Remilia Scarlet

      I've been alternating. I block out areas and test the gameplay for a day or two, then I spend two days detailing. Basically how I've always worked, but I've extended it a bit.

  5. unknown.png

     

    99.9% done with the world geometry.  Now to finish scripting and bug squashing, then test everything all over again.

    1. Show previous comments  3 more
    2. Avoozl

      Avoozl

      Well we can't all be slaughtermap enthusiasts. :P

    3. Nine Inch Heels

      Nine Inch Heels

      A couple hundred cybies and Viles a Slaughtermap don't make.

      Depending on use and placement those are.

       

      Grammar from Yoda learned I have.

    4. riderr3

      riderr3

      My biggest is about 1100 sectors, but I am feeling tired to test such a complex maps.

  6. buttons.png.00a36ec4ddfaee68b27c29f42de4aa38.png

    Wait... wait... how long have these buttons been here in GZDoom Builder?!

    1. Misty

      Misty

      As far I as first time booted gzdoom builder few years ago? 

    2. Remilia Scarlet
  7. A new unfinished room in my Quake map.  I think I'm finally starting to get comfortable with 3D level editing.  Still not sure what I'm going to call the level, though.

     

    SR59pPf.png

     

    Having this reference for lighting has helped me tremendously as well.

    1. Lila Feuer

      Lila Feuer

      I love it, it looks fucking evil.

    2. leodoom85

      leodoom85

      Quote

      Still not sure what I'm going to call the level, though.

      idk...call it Inner Fear or something similar ;)

  8. Ale and Ginger Chicken.  Super simple, super old, but super tasty.

    • 2 boneless chicken breasts
    • 2 cups water
    • 12oz ale (I use Alaskan Amber)
    • 1/2 rounded tsp black pepper
    • 1/4 tsp salt
    • 2 1/2 tsp powdered ginger
    • 10-20 threads saffron, OR 1/2 tsp paprika and 1/4 tsp turmeric
    • 1 tbsp butter
    • Corn starch, for thickening
    1. Heat up a pressure cooker without the lid.  Add butter.  Once melted, add the chicken and sear on all sides.
    2. Add everything else, mix well.
    3. Cover pressure cooker and cook for 30 minutes.
    4. Realize 10 minutes in you forgot the saffron.  Uncover, add, then restart the pressure cooker.
    5. Use corn starch mixed with a bit of water to thicken
    6. Serve with rice.
    1. Show previous comments  1 more
    2. Remilia Scarlet

      Remilia Scarlet

      Actually, after making this quite a few times, I think I'm going to adjust my recipe and put in double the pepper and salt.  Otherwise it came out good as always.

    3. Zakken

      Zakken

      Came to Doomworld for the Doom content; stayed for the culinary recipes.

    4. Fonze

      Fonze

      Yes 

      Pics please; my eyes hunger for the foodpr0ns ;p

  9. Common Lisp only "kinda" has Enumerated types.  Normally instead of doing something like this in C

    typedef enum {
      tiger,
      snow_leopard,
      pandaren,
      worgen
    } fursonas;

    You'd instead do something like this:

    (deftype fursonas ()
      '(member :tiger :snowleopard :pandaren :worgen))

    Unfortunately you can't do stuff exactly like this natively:

    typedef enum {
      nosona = -1
      tiger = 0,
      snow_leopard,
      pandaren ,
      worgen,
      NUM_FURSONAS
    } fursonas;

    The closest you could get is to use constants and a global variable:

    (alexandria:define-constant +no-sona+     -1)
    (alexandria:define-constant +tiger+        0)
    (alexandria:define-constant +snow-leopard+ 1)
    (alexandria:define-constant +pandaren+     2)
    (alexandria:define-constant +worgen+       3)
    (defparameter *num-fursonas* 5)

    I don't particularly like this since I have to manually count the constants to give a value to the global variable.  There's also the issue of typing.

     

    Thankfully Lisp is homoiconic and has kick-ass macros. 

    (defmacro defenum (type-name (&rest values) &key super-type make-length
                                                  (package *package*))
      (let ((ret nil)
            (start-val 0)
            (sym nil)
            (valid-vals nil))
        
        (dolist (val values)
          (if (not (symbolp val))
              (cond
                ((not (consp val))
                 (error "Enum values must be symbols or CONSes"))
    
                ((not (symbolp (car val)))
                 (error "Enum names must be a symbol"))
    
                ((not (or (numberp (cdr val))
                          (numberp (cadr val))))
                 (error "The CDR of an Enum's value must be a number"))
    
                (t
                 (setf sym (car val))
                 (setf start-val (if (numberp (cdr val))
                                     (cdr val)
                                     (cadr val)))))
    
              (setf sym val))
    
          (setf ret
                (nconc ret
                       (list (list 'alexandria:define-constant
                                   (find-symbol (symbol-name sym) package)
                                   start-val
                                   :test (quote 'equal)))))
    
          (if (find start-val valid-vals :test #'=)
              (error "Duplicate enum value")
              (push start-val valid-vals))
    
          (incf start-val))
    
        (setf valid-vals (sort valid-vals #'<))
    
        (when make-length
          (setf ret
                (nconc ret
                       (list `(defparameter ,(intern (string-upcase
                                                      (format nil "*num-~a*" type-name))
                                                     package)
                                (1- (length
                                     #+sbcl  (sb-ext:typexpand (quote ,type-name))
                                     #+clisp (ext:type-expand  (quote ,type-name))
                                     #+ccl   (ccl::type-expand (quote ,type-name)))))))))
    
        `(progn
           (deftype ,type-name ,super-type (quote (member ,@valid-vals)))
           ,@ret)))

    This doesn't totally get around the typing issue, but it's a step in the right direction.  It does at least allow me to do the whole sequential value thing, though, and can automatically update the global variable (if you even want one) as-needed.  Thus I can do this:

    (defenum my-enum/fursonas
      (+tiger+
       +snow-leopard+
       +pandaren+
       +worgen+))
    
    ;; Or for another example...
    
    (defenum bt-buttons
        ((+bt-no-button+ -1)
         (+bt-attack+     0)
          +bt-strafe+
          +bt-run+
          +bt-use+
          +bt-ready-knife+
          +bt-ready-pistol+
          +bt-ready-machine-gun+
          +bt-ready-chaingun+
          +bt-next-weapon+
          +bt-prev-weapon+
          +bt-esc+
          +bt-pause+
          +bt-strafe-left+
          +bt-strafe-right+
          +bt-move-forward+
          +bt-move-backward+
          +bt-turn-left+
          +bt-turn-right+)
      :make-length t)

    Again, not perfect, but close, and Type Safe Enough For Me™.  If you're curious, this macro expands at compile time (because yes, Common Lisp is (usually) compiled to machine code) to this:

    (PROGN
     (DEFTYPE BT-BUTTONS ()
       '(MEMBER -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17))
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-NO-BUTTON+ -1 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-ATTACK+ 0 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-STRAFE+ 1 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-RUN+ 2 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-USE+ 3 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-READY-KNIFE+ 4 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-READY-PISTOL+ 5 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-READY-MACHINE-GUN+ 6 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-READY-CHAINGUN+ 7 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-NEXT-WEAPON+ 8 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-PREV-WEAPON+ 9 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-ESC+ 10 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-PAUSE+ 11 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-STRAFE-LEFT+ 12 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-STRAFE-RIGHT+ 13 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-MOVE-FORWARD+ 14 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-MOVE-BACKWARD+ 15 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-TURN-LEFT+ 16 :TEST 'EQUAL)
     (ALEXANDRIA.0.DEV:DEFINE-CONSTANT +BT-TURN-RIGHT+ 17 :TEST 'EQUAL)
     (DEFPARAMETER *NUM-BT-BUTTONS* (1- (LENGTH (SB-EXT:TYPEXPAND 'BT-BUTTONS)))))

    Not 100% the same, but close enough for me and my needs.

  10. Dang it, I feel torn between writing creepy dark ambient music for area one of my Doom map like I did for SoTNR, something harsh and industrial like I did for Extreme Terror, or something along the lines of Lost Years or one of John Carpenter's recent songs.

     

    Anyone have some suggestions?  Do you think synthwave-y stuff would even sound appropriate for a Doom map like mine?  I'm kinda leaning towards it only because my Doom map is me experimenting with a bunch of stuff anyway, but I'm not opposed to doing other styles.  Plus there'll be four separate tracks in the whole level, so it's not like I'm stuck with one style.

    1. Show previous comments  9 more
    2. Remilia Scarlet

      Remilia Scarlet

      Yeah it's the same as Aeolian, so a scale goes W H W W H W W.  I generally chose a minor key then work off of a natural minor scale when I write music.

       

      The phrygian things are used in a lot of EBM/industrial and goa trance since they can sound very dark, depending on the parts of it that are emphasized.  They also fit in nicely with diminished chords, but can also have the occasional minor third to fit in with minor chords and change the feel slightly.

       

      For example, I'm pretty sure I based this song around a phrygian dominant scale (glad I uploaded this yesterday lol):

       

      And this goa trance song, which I haven't officially released yet:

      https://www.partition36.com/random-stuff/in-a-dream.mp3

    3. Major Arlene

      Major Arlene

      I think something like Excessive Exposure w/o vocals would be really awesome! Although I'd probably be dancing too much to play properly :3

    4. Remilia Scarlet

      Remilia Scarlet

      Well I went through all my WIP songs (and whoa howdy do I always have a lot of them), and found two synthwave-y songs that would fit, and an industrial/Extreme Terror-style one that would work.  I'll work on these in the next few days and see if I can't get rough mixes done just to see if they will indeed feel right in the level.

       

      Either way, they'll be on my next album, so it's not like the work will go to waste :-P

  11. Did another complete playthrough of Umbra of Fate today just for fun. On UV, it took me about an hour and 20 minutes total, and I died 46 times XD

     

    *sigh* 4 more days and it'll be out :^)

    1. bzzrak

      bzzrak

      Waiting for ZeroMaster's NM-Speed in 23:25

      You read it here first! :] 

    2. Remilia Scarlet

      Remilia Scarlet

      NM is directly accessible this time, too!

  12. Did some more work on a level I've been working on since last year and took a screenshot of the unfinished room I added tonight:

    8w13of7.png

     

    I have no idea when this thing will be out since I seem to work on it only in bursts.  It could be in two months, it could be December for all I know :-P

    1. Nine Inch Heels

      Nine Inch Heels

      Some trippy lighting, makes me want to play a nice chiptune instantly. Seems to be quite difficult to get all that done according to how it's been imagined.

  13. Earlier today I finished some early versions of the music that'll be going into my Doom level.  There'll be one track for each major area, plus a short "outside ambiance" sort of thing, so five total tracks.

     

    Anyway, I put them into the pk3, wrote the ACS to fade them nicely and control them as-needed, fired up GZDoom and... holy crap it's like 300% creepier now.  I'm seriously glad I usually playtest without the music.

     

    The ambient outdoor thing isn't much, just a sound collage of some low rumbling and a recording of a creepy dog barking I found.  I layered in some reverb via a convolution plugin using a canyon impulse, which makes it sound sounds pretty solid and very outdoors-y.

     

    The outer temple area uses a song I'm calling "Drive Them Back" (named after a Vincent Price sample I use in it), which is a dark, gothic track that uses a melancholy ARP Solina for the melody.  Sorta like SoTNR map 03's music, but less evil and more gloomy.

     

    The crypt area has a track that's much more experimental and ambient.  It features a droning background of noise and warmth, interspersed demonic noises, and some algorithmically generated drums that get all messed up and don't play the role of a standard rhythm track.

     

    I'm not sure exactly what I'll be doing for the inner temple or the final area, but I noticed that Skinny Puppy's album "Cleanse Fold and Manipulate" went pretty well with the general feel so far, so I might experiment more with distorted drums and sound collage textures for them.

    1. Show previous comments  1 more
    2. Nine Inch Heels

      Nine Inch Heels

      This will be interesting to listen to for sure.

       

      Remember when you said MIDIs had their place? I feel the same way about mp3, actually. I think that, looking at these highly detailed GZDoom maps you and others make, a MIDI would feel out of place there. I can totally see why people would want for their highly detailed and styled maps to have what they feel is the absolute best fitting OST, in which case MIDIs probably just can't cut it for their inherent limitations.

       

      At the same time, looking at the more "basic" aesthetics of non-GZDoom stuff, mp3s would be out of place there for me as well, since relatively high definition tracks would make the environment seem a lot more comical than it already is.

       

      Anyway... Looking forward to what you're composing there. :-)

    3. Remilia Scarlet

      Remilia Scarlet

      Hmm... comical...

       

      *plans to use Yakety Sax in a vanilla map*

    4. Nine Inch Heels

      Nine Inch Heels

      ^ROFL

       

      I will throw this MIDI into a map at some point:

       

      Gradius III cosmo-plant.zip

       

      Hard to resist...

  14. Fellow Skinny Puppy fans might enjoy this...

     

    https://www.youtube.com/watch?v=gKY0nwUrTXA

    1. Khorus

      Khorus

      Bloody hell yes! Al Jourgensen too.

  15. Freaky Panties is like 90% done now.  All I need to do is finish detailing two areas, then of course do a lot of testing myself.  I'll also need some playtesters, so if you're interested, hit me up.  I'll post again asking for playtesters in a few days on the actual WIP thread.

     

    It looks like it'll top out at about 5.5k sectors, which is interesting because that's almost half the number of sectors in Umbra of Fate.  Yet Freaky Panties has taken me only about a month to build.  ¯\_(ツ)_/¯

     

    I've also gotten some more work done on two separate Quake levels.  One needs some reworking gameplay wise, and has a gothic theme reminiscent of UoF and SoTNR.  The other is an Aztec-themed temple and is still quite early in development, but will be sort of similar to Freaky Panties in how it starts out from a hub-like area.

  16. Freaky Panties will be out in about a week. There's just some last minute things, like running it through my tool to remove unused textures and final detailing tweaks. Also finalizing the music, though for that I'm waiting on my car to be fixed, oddly enough.  See, when I do the final mixing and mastering of my music, I use the subwoofers in my car to gauge the low end. Without them, I feel lost.

     

    Anyway, about a week and it'll be out.

  17. Had a sudden urge to hack today, so I very quickly hacked together a parser for UDMF in Common Lisp just for the hell of it.  Not the cleanest code, but it works well enough.  I still hope to eventually release this whole Waddle library of mine someday...

     

    My testing so far, using my current map which has about 5.5mb of UDMF data, shows that it parses the raw text into a preliminary raw form in about 0.3-0.4 seconds on a Core2Quad @2.4GHz.  A full parse from raw text to final form takes 0.7-0.8 seconds.  Still needs some optimization in some places, I think, especially in the second pass.

     

    Here's the bit of code I was using to check that it worked and get a very basic idea of performance:

    (asdf:load-system :waddle)
    (in-package :waddle)
    
    (let* ((wad (load-wad-file #P"/mnt/storage/alexa-extended/bin/games/doom/Mine/raven/raven01.wad"))
           (level (get-lump wad "TEXTMAP" :as-text t :return-data t)))
      (time (elt (parse-udmf (parse-udmf->ast level)) 42)))

     

    Here's the actual code, minus all the DEFSTRUCT calls to create the structs that hold stuff.

    ;;;; Waddle
    ;;;; Copyright (C) 2017 Alexa Jones-Gonzales  <alexa@partition36.com>
    ;;;;
    ;;;; This program is free software: you can redistribute it and/or modify
    ;;;; it under the terms of the GNU General Public License as published by
    ;;;; the Free Software Foundation, either version 3 of the License, or
    ;;;; (at your option) any later version.
    ;;;;
    ;;;; This program is distributed in the hope that it will be useful,
    ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
    ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ;;;; GNU General Public License for more details.
    ;;;;
    ;;;; You should have received a copy of the GNU General Public License
    ;;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;;
    ;;;; Parser for UDMF data
    ;;;;
    ;;;; I've optimized this code as well as I can, but a few things are
    ;;;; lacking. Most glaring is that errors do not report the line or
    ;;;; column number during parsing.
    ;;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    (in-package :p36.waddle)
    
    (eval-when (:compile-toplevel)
      (declaim (optimize (debug 0) (safety 1) (compilation-speed 0))))
    
    (defparameter *udmf-line-regex*
      (let ((cl-ppcre:*regex-char-code-limit* 256)
            (cl-ppcre:*use-bmh-matchers* t))
        (cl-ppcre:create-scanner "[;{}]" :single-line-mode t)))
    
    (defmacro get-valid-int (the-str)
      "Parses THE-STR for a valid integer and returns that integer, or nil
    if it could not be parsed."
      (let ((the-num (gensym))
            (parsed-len (gensym)))
        `(when (not (find #\. ,the-str :test #'eql))
           (multiple-value-bind
                 (,the-num ,parsed-len)
               (parse-integer ,the-str :junk-allowed t)
             (when (= ,parsed-len (length ,the-str))
               ,the-num)))))
    
    ;;
    ;; These constants make identifier parsing a bit faster
    ;;
    (alexandria:define-constant +ascii-num-min+   48)
    (alexandria:define-constant +ascii-num-max+   57)
    
    (alexandria:define-constant +ascii-upper-min+ 65)
    (alexandria:define-constant +ascii-upper-max+ 90)
    
    (alexandria:define-constant +ascii-lower-min+ 97)
    (alexandria:define-constant +ascii-lower-max+ 122)
    
    (alexandria:define-constant +ascii-_-code+    95)
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;
    ;;; UDMF Parser Class
    ;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    (defstruct udmf-parser
      (source "" :type simple-string)
      (line   0  :type fixnum)
      (column 0  :type fixnum))
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;
    ;;; AST Classes
    ;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    (defstruct udmf-raw-block
      (name "" :type simple-string)
      (expressions (make-array 32 :element-type 'cons :adjustable t :fill-pointer 0 :initial-element '(nil))
                   :type (vector cons)
                   :read-only t))
    
    (defstruct udmf-variable
      (name "" :type simple-string)
      (value 0 :type (or simple-string fixnum float boolean)))
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;
    ;;; UDMF Parsing Condition Stuff
    ;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    (define-condition udmf-parse-error (error)
      ((text
        :initarg :text
        :type string
        :reader text
        :initform "Could not parse UDMF data")
    
       (parser
        :initarg :parser
        :type (or udmf-parser null)
        :reader parser
        :initform nil)
    
       (data
        :initarg :data
        :type string
        :initform "(no data given)"
        :reader data)))
    
    (define-condition udmf-parse-error/invalid-identifier (udmf-parse-error)
      ())
    
    (define-condition udmf-parse-error/invalid-string (udmf-parse-error)
      ())
    
    (define-condition udmf-parse-error/invalid-assignment (udmf-parse-error)
      ())
    
    (define-condition udmf-parse-error/invalid-block (udmf-parse-error)
      ())
    
    (defmethod print-object ((obj udmf-parse-error) out)
      (let ((line "(unknown)")
            (line-num "?"))
    
        (when (parser obj)
          (setf line (udmf-parser-line (parser obj)))
          (setf line-num (udmf-parser-line (parser obj))))
    
        (format out "Error: ~a~%Data: ~s~%Line #~a: ~s"
                (text obj) (data obj)
                (if (numberp line-num)
                    (1+ line-num)
                    line-num)
                line)))
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;
    ;;; UDMF Parsing
    ;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    ;;
    ;; We define our own floating point parser since we don't need a lot
    ;; of fancy features.  Just plain decimal notation.
    ;;
    ;; NOTE: Actually, UDMF allows for a bit more notation, such as
    ;; 12.4e20.  But as the maps I've tested this on don't have that
    ;; notation, I haven't implemented it yet.  When I find one, or
    ;; someone needs it, I will.
    ;;
    (defun parse-udmf-float (str &optional (error-on-int t) nil-on-error)
      "Parses a string into a floating point number, as defined by UDMF.
    Strings that have valid integers are converted into floating point
    numbers only if ERROR-ON-INT is non-NIL.  If NIL-ON-ERROR is non-NIL,
    then a value of NIL is returned rather than raising an ERROR."
      (declare (type simple-string str)
               (optimize (speed 3) (safety 0) (debug 0)))
    
      (macrolet
          ((error-or-return (&optional (msg "Could not parse UDMF float"))
             `(if nil-on-error
                  (return-from parse-udmf-float nil)
                  (error ,msg))))
    
        (let ((decimal-pos 0)
              (int-str "")
              (dec-str "")
              (int-part 0)
              (dec-part 0)
              (sign 1))
          (declare (type fixnum decimal-pos sign)
                   (type (or fixnum null) int-part dec-part)
                   (type simple-string int-str dec-str))
    
          ;; Check for integers
          (when (and (get-valid-int str) error-on-int)
            (error-or-return "Could not parse UDMF float: string represents a valid integer"))
    
          ;; Check for negative sign and adjust the number accordingly
          (when (find (elt str 0) #(#\+ #\-) :test #'eql)
            (cond
              ((eql (elt str 0) #\-)
               (setf sign -1)
               (setf str (subseq str 1)))
    
              ((eql (elt str 0) #\+)
               (setf str (subseq str 1)))))
    
          ;; Search for a decimal point
          (when (not (setf decimal-pos (position #\. str :test #'eql)))
            (error-or-return))
    
          ;; Split at the decimal point
          (setf int-str (subseq str 0 decimal-pos))
          (setf dec-str (subseq str (1+ decimal-pos)))
    
          ;; Check to make sure each part has a valid integer
          (setf int-part (get-valid-int int-str))
          (setf dec-part (get-valid-int dec-str))
          (when (or (not int-part) (not dec-part))
            (error-or-return "Could not parse UDMF float, junk found in value"))
    
          ;; Return a floating point made from the parts
          (* sign (coerce (+ (coerce int-part 'float)
                             (* (coerce dec-part 'float)
                                (coerce (expt 10 (coerce (- (length dec-str)) 'float)) 'float)))
                          'float)))))
    
    (defun parse-udmf-identifier (token)
      (declare (type simple-string token)
               (optimize (speed 3) (safety 0) (debug 0)))
    
      (let ((char1 (char-code (elt token 0))))
        (declare (type (unsigned-byte 8) char1))
    
        (when (and (or (< char1 +ascii-upper-min+)
                       (> char1 +ascii-upper-max+))
                   (or (< char1 +ascii-lower-min+)
                       (> char1 +ascii-lower-max+))
                   (not (= char1 +ascii-_-code+)))
          (error 'udmf-parse-error/invalid-identifier
                 :text "Invalid UDMF identifier" :data token))
    
        ;; Check the rest of the characters
        (dotimes (i (1- (length token)))
          (setf char1 (char-code (elt token i)))
          (when (and (or (< char1 +ascii-num-min+)
                         (> char1 +ascii-num-max+))
                     (or (< char1 +ascii-upper-min+)
                         (> char1 +ascii-upper-max+))
                     (or (< char1 +ascii-lower-min+)
                         (> char1 +ascii-lower-max+))
                     (not (= char1 +ascii-_-code+)))
            (error 'udmf-parse-error/invalid-identifier
                   :text "Invalid UDMF identifier" :data token)))
    
        token))
    
    (defun parse-udmf-value (token)
      (declare (type simple-string token)
               (optimize (speed 3) (safety 0) (debug 0)))
    
      (cond
        ((and (eql (elt token 0) #\")
              (eql (elt token (1- (length token))) #\"))
         (return-from parse-udmf-value (subseq token 1 (1- (length token)))))
    
        ((or (and (eql (elt token 0) #\")
                  (not (eql (elt token (1- (length token))) #\")))
             (and (not (eql (elt token 0) #\"))
                  (eql (elt token (1- (length token))) #\")))
         (error 'udmf-parse-error/invalid-string
                :text "Malformed string value" :data token)))
    
      (when (string= token "true")
        (return-from parse-udmf-value t))
    
      (when (string= token "false")
        (return-from parse-udmf-value nil))
    
      (or (get-valid-int token)
          (parse-udmf-float token)))
    
    (defun parse-udmf-assignment-expr (line)
      (declare (type simple-string line)
               (optimize (speed 3) (safety 0) (debug 0)))
    
      (when (not (eql (elt line (1- (length line))) #\;))
        (error 'udmf-parse-error/invalid-assignment
               :text "Unexpected end of assignment" :data line))
    
      (let ((pos-of-= (or (position #\= line :test #'eql)
                          (error 'udmf-parse-error/invalid-assignment
                                 :text "No equal sign found in assignment" :data line)))
            (ident "")
            (value ""))
        (declare (type (or fixnum null) pos-of-=)
                 (type simple-string ident value))
    
        (setf ident (parse-udmf-identifier (string-trim '(#\Space)     (subseq line 0 pos-of-=))))
        (setf value (parse-udmf-value      (string-trim '(#\Space #\;) (subseq line (1+ pos-of-=)))))
        (cons ident value)))
    
    (defun parse-udmf-block (line stream)
      (declare (type string-stream stream)
               (type simple-string line)
               (optimize (speed 3) (safety 0) (debug 0)))
    
      (let* ((ret (make-udmf-raw-block :name (parse-udmf-identifier (string-trim '(#\Space #\{) line)))))
        (declare (type simple-string line)
                 (type udmf-raw-block ret))
    
        ;; We now start parsing assignment expressions until we hit a }
        (loop
           do (progn
                (handler-case
                    (setf line (read-line stream))
                  (end-of-file ()
                    (error 'udmf-parse-error/invalid-block
                           :text "Unclosed block encountered" :data line)))
    
                (when (string= line "}")
                  (return))
    
                (unless (string= line "{")
                  (vector-push-extend (parse-udmf-assignment-expr line)
                                      (udmf-raw-block-expressions ret)))))
    
        ret))
    
    (defmacro parse-udmf-line-into-array (line arr stream)
      (declare (type simple-string line)
               (type string-stream stream)
               (type vector arr)
               (optimize (speed 3) (safety 0) (debug 0)))
    
      `(if (and (eql (elt ,line (1- (length ,line))) #\;)
                (position #\= ,line :test #'eql))
           (vector-push-extend (parse-udmf-assignment-expr ,line) ,arr)
           (vector-push-extend (parse-udmf-block line ,stream) ,arr)))
    
    (defun parse-udmf->ast (udmf)
      (declare (type string udmf)
               (optimize (speed 3) (safety 0) (debug 0)))
    
      (let ((parser (make-udmf-parser :source udmf))
            (ret (make-array 2 :element-type '(or udmf-raw-block cons)
                             :adjustable t :fill-pointer 0 :initial-element '(nil)))
            (line "")
            (sub-line "")
            (last-end 0))
        (declare (type simple-string line sub-line)
                 (type fixnum last-end)
                 (type udmf-parser parser)
                 (type (vector (or udmf-raw-block cons)) ret))
    
        (handler-case
            (with-input-from-string (in (udmf-parser-source parser))
              (loop do
                   (progn
                     (setf line (read-line in))
    
                     ;; Ignore blank lines
                     (when (not (string= line ""))
                       (setf last-end 0)
    
                       ;; We only use CL-PPCRE:DO-SCANS if we absolutely need to
                       (if (> (count-if #'(lambda (char) (char= char #\; #\{ #\})) line) 1)
                           (cl-ppcre:do-scans (mstart mend regs rege *udmf-line-regex* line)
                             (setf sub-line (string-trim '(#\Space) (subseq line last-end mend)))
                             (setf last-end mend)
    
                             (when (not (string= sub-line ""))
                               (parse-udmf-line-into-array line ret in)))
    
                           (parse-udmf-line-into-array line ret in))))))
    
          (end-of-file ()
            ret))))
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;
    ;;; UDMF AST -> UDMF Structures Translation
    ;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    ;;
    ;; This will create a function to take a raw UDMF block and turn it
    ;; into a UDMF structure.
    ;;
    (defmacro create-udmf-raw-block->udmf-struct-translator (fn-name type)
      (declare (optimize (debug 0) (compilation-speed 0)))
    
      (let ((waddle-pkg (find-package :p36.waddle))
            (fn-arg (gensym "FN-ARG-"))
            (ret (gensym "FN-RET-"))
            (expr (gensym "FN-EXPR-"))
            (type-accessor-prefix (concatenate 'string
                                               (subseq (string type)
                                                       (1+ (position #\/ (string type)
                                                                     :test #'eql
                                                                     :from-end t)))
                                               "-")))
    
        `(defun ,fn-name (,fn-arg)
           (let ((,ret (,(intern (concatenate 'string "MAKE-" (string type)))
                         :name (udmf-raw-block-name ,fn-arg))))
    
             (loop for ,expr cons across (udmf-raw-block-expressions ,fn-arg) do
                  (cond
                    ,@(loop for slot in (closer-mop:class-slots (find-class type))
                        when (equal waddle-pkg (symbol-package (closer-mop:slot-definition-name slot)))
                        collecting
                          (let* ((slot-name-str (string-downcase (string (closer-mop:slot-definition-name slot))))
                                 (slot-accessor (intern (concatenate 'string type-accessor-prefix (string-upcase slot-name-str)))))
                            `((string= ,slot-name-str (car ,expr))
                              (setf (,slot-accessor ,ret) (cdr ,expr)))))))
    
             ,ret))))
    
    ;;
    ;; Create all the functions needed to turn raw UDMF blocks into UDMF structures
    ;;;
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-vertex udmf-block/vertex)
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-linedef udmf-block/linedef)
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-sidedef udmf-block/sidedef)
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-sector udmf-block/sector)
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-thing udmf-block/thing)
    
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-zdoom-vertex udmf-block/zdoom/vertex)
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-zdoom-linedef udmf-block/zdoom/linedef)
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-zdoom-sidedef udmf-block/zdoom/sidedef)
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-zdoom-sector udmf-block/zdoom/sector)
    (create-udmf-raw-block->udmf-struct-translator udmf-raw-block->udmf-zdoom-thing udmf-block/zdoom/thing)
    
    ;;
    ;; Parses the AST generated by PARSE-UDMF->AST into a vector of UDMF
    ;; structures and CONSes.  In the resulting array, CONSes represent
    ;; toplevel assignments.
    ;;
    (defun parse-udmf (udmf-ast)
      (declare (type (vector (or cons udmf-raw-block)) udmf-ast)
               (optimize (speed 3) (debug 0)))
    
      (let ((ret (make-array (length udmf-ast) :adjustable t :fill-pointer 0))
            (block-name "")
            (zdoom nil)
            (thing '(nil)))
        (declare (type simple-string block-name)
                 (type vector ret)
                 (type boolean zdoom)
                 (type (or cons udmf-raw-block) thing))
    
        (dotimes (i (length udmf-ast))
          (setf thing (elt udmf-ast i))
    
          (typecase thing
            (cons
             (format t "~a: ~a~%" (car thing) (cdr thing))
    
             (when (and (string= (the string (car thing)) "namespace")
                        (or (string= (string-downcase (cdr thing)) "zdoom")
                            (string= (string-downcase (cdr thing)) "zdoomtranslated")))
               ;; We're in ZDoom, so keep track of this so we can also
               ;; call ZDoom translation functions
               (setf zdoom t))
    
             (vector-push-extend thing ret))
    
            (udmf-raw-block
             (setf block-name (udmf-raw-block-name thing))
    
             (if zdoom
                 (cond
                   ((string= "thing" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-thing thing) ret))
    
                   ((string= "linedef" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-linedef thing) ret))
    
                   ((string= "sidedef" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-sidedef thing) ret))
    
                   ((string= "vertex" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-vertex thing) ret))
    
                   ((string= "sector" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-sector thing) ret))
    
                   (t
                    (format t "Don't know how to handle a ~a block~%" block-name)))
    
                 (cond
                   ((string= "thing" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-zdoom-thing thing) ret))
    
                   ((string= "linedef" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-zdoom-linedef thing) ret))
    
                   ((string= "sidedef" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-zdoom-sidedef thing) ret))
    
                   ((string= "vertex" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-zdoom-vertex thing) ret))
    
                   ((string= "sector" block-name)
                    (vector-push-extend (udmf-raw-block->udmf-zdoom-sector thing) ret))
    
                   (t
                    (format t "Don't know how to handle a ~a block~%" block-name)))))))
    
        ret))
    

     

    1. Show previous comments  2 more
    2. Phade102

      Phade102

      Ahh I get it. thats really interesting. I'm not really a big fan of scripts, but I believe I do remember using a very simple one a long long time ago to place fog in a level.

       

      As you said though, this isn't a script. it'd be interesting to see where this program can be used.

    3. Rosh Fragger

      Rosh Fragger

      I mostly play Watch Dogs 2 in such urges lol

    4. Csonicgo

      Csonicgo

      Wonderful work!

       

  18. Here's a partial (like 1/6th) playthrough of my new Doom level.  This includes new music written by me specifically for this map.  What's left now is balancing out the difficulty levels, minor detailing/fixing here and there, and some additional scripting that's needed to get it to fit within the rest of the WAD.  And of course, the third level :-P

     

     

    1. Show previous comments  6 more
    2. Nine Inch Heels

      Nine Inch Heels

      You'd really allow me to help you playtest that project of yours? I mean, really "really"? Now I'm really "nervous", and flattered too.

    3. TwinBeast

      TwinBeast

      Like the colors, music. Felt more Quake like than Quake. Didn't even surprise me to see the flying Wizard.

    4. sesq

      sesq

      hmu when you're done with it

       

      pleeeeeaaase

  19. Here's my own playthrough of Shadows of The Nightmare Realm.  Well, the first level of it anyway.  I'll be working on the other levels in the next few days.

     

    You can enable the subtitles if you want to see some occasional commentary.

     

     

    1. rdwpa

      rdwpa

      Cool, this will answer a lot of questions. :) 

    2. Tracer

      Tracer

      This looks amazing.

    3. GarrettChan

      GarrettChan

      Nice, I like how you admire your own work occasionally ;P   I would like to read/listen to more commentary.

  20. Huh... could have sworn I opened up Trenchbroom and not GZDB...

     

    image.png.638cd0de19b263c068146621bea1fc22.png

  21. I finally finished the layout and core design of my first Quake map.  That I would ever get this far in making one is something I never thought I'd see.  From here I need to adjust the difficulty settings, add some additional detailing, and then playtest the hell out of it.  Then it'll be finished :^)

     

    Right now it's sitting at about 6600 brushes and 130 enemies on hard.  I have a feeling I could optimize that brush count a bit.

  22. I haven't worked on my Quake map in quite a while, so I figured I'd take a break from Doom for a bit.

     

    7JAxECL.png

    1. Nevander

      Nevander

      That looks awesome. Love the lighting.

×