So I woke up this morning, saw this thread and thought, 'Hey it'd be cool if I could actually play the levels posted here'. I figured I could make a sandbox level that I could just paste into and be able to test the level. Unfortunately I couldn't get Level.place_tile to work with items and mobs, which got me thinking about writing a script to do the conversion. At that point I realized that more than just linux users might want to try this out so I settled on using racket, here is what I've come up with:
#lang racket
;Phobos testing v0.1
(require racket/gui/base)
(define luaTop
(list "Levels(\"CUSTOM\",{"
"name = \"Phobos Test\","
"mortem = \"\","
"mortem_desc = \"in testing\","
"Create = function ()"
"Level.fill(\"wall\")"
"local translation = {"
"['.'] = \"rock\","
"[','] = \"floor\","
"['#'] = \"wall\","
"['+'] = \"door\","
"['>'] = \"stairs\","
"['*'] = \"floor\","
"['P'] = \"floor\","
"['H'] = \"floor\","
"['1'] = \"floor\","
"['2'] = \"floor\","
"['3'] = \"floor\","
"['4'] = \"floor\""
"}"
"local center = [["
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"............................................................................"
"]]"
"local map = [["))
(define luaMiddle
(list "]]"
"Level.place_tile(translation, center, 2, 2 )"
"Level.place_tile(translation, map, 9, 3 )"
""))
(define luaBottom
(list "end,"
"OnEnter = function ()"
"player.eq.weapon = item.new(\"pistol\")"
"end,"
"OnKill = function ()"
"end,"
"OnKillAll = function ()"
"end,"
"OnExit = function ()"
"levels.CUSTOM.mortem = \"fled alive the trials\""
"end,"
"})"))
(define frontal #t)
(define currentDiff 3)
(define diffVec
(vector
#hash(("*" . "Level.drop_item(\"smed\", coord.new(")
("P" . "Level.drop_item(\"garmor\", coord.new(")
("H" . "Level.drop_being(\"sergeant\", coord.new(")
("1" . "Level.drop_being(\"former\", coord.new("))
#hash(("*" . "Level.drop_item(\"smed\", coord.new(")
("P" . "Level.drop_item(\"garmor\", coord.new(")
("H" . "Level.drop_being(\"sergeant\", coord.new(")
("1" . "Level.drop_being(\"former\", coord.new(")
("2" . "Level.drop_being(\"former\", coord.new("))
#hash(("*" . "Level.drop_item(\"smed\", coord.new(")
("P" . "Level.drop_item(\"garmor\", coord.new(")
("H" . "Level.drop_being(\"sergeant\", coord.new(")
("1" . "Level.drop_being(\"former\", coord.new(")
("2" . "Level.drop_being(\"former\", coord.new(")
("4" . "Level.drop_being(\"former\", coord.new("))
#hash(("*" . "Level.drop_item(\"smed\", coord.new(")
("P" . "Level.drop_item(\"garmor\", coord.new(")
("H" . "Level.drop_being(\"sergeant\", coord.new(")
("1" . "Level.drop_being(\"former\", coord.new(")
("2" . "Level.drop_being(\"former\", coord.new(")
("3" . "Level.drop_being(\"former\", coord.new(")
("4" . "Level.drop_being(\"sergeant\", coord.new("))))
(define (pass-inspection aString)
((lambda (stringList)
(and (= (length stringList) 15)
(null? (filter (compose not
(curry = 55)
string-length)
stringList))))
(regexp-split #rx"\n" (regexp-replace #rx"\r" aString ""))))
(define (generate-level aButton anEvent)
(call-with-values (lambda () (values (send tehArea get-value)
(open-output-file (send fileName get-value)
#:mode 'text
#:exists 'replace)))
(lambda (mapArea newPort)
(cond [(and (pass-inspection mapArea))
(begin
(write-string
(string-append (string-join luaTop "\n")
mapArea
(string-join luaMiddle "\n")
(if frontal "Level.player(75,10)\n" "Level.player(2,2)\n")
(place-stuff mapArea (vector-ref diffVec currentDiff))
(string-join luaBottom "\n")) newPort)
(close-output-port newPort))]
[else
((lambda (popUp)
(new message%
[label "The map should be 55x15"]
[parent popUp])
(send popUp show #t))
(new dialog%
[label "Error - map size"]))]))))
(define (change-difficulty aPos aButton anEvent)
(begin
(send (vector-ref buttonVec currentDiff) enable #t)
(set! currentDiff aPos)
(send (vector-ref buttonVec currentDiff) enable #f)))
(define (spot->string aNum)
(string-append (number->string (+ 9 (remainder aNum 56))) "," (number->string (+ (quotient aNum 56) 3))))
(define (place-stuff aString aHash)
(apply string-append
(filter (negate null?)
(map (lambda (aCons)
((lambda (hashRet)
(if (= (string-length hashRet) 0) null
(string-append hashRet
(spot->string (car aCons)) "))\n")))
(hash-ref aHash (substring aString (car aCons) (cdr aCons)) "")))
(regexp-match-positions* #rx"[\\*PH1-4]" aString)))))
(define tehFrame
(new frame%
[label "Phobos base testing"]
[width 700]
[height 450]))
(define outerBox
(new vertical-panel%
[parent tehFrame]))
(define buttonBox
(new horizontal-panel%
[stretchable-height #f]
[parent outerBox]))
(define difficultyBox
(new horizontal-panel%
[stretchable-height #f]
[parent outerBox]))
(define goButton
(new button%
[label "Generate level"]
[callback generate-level]
[parent buttonBox]))
(define fButton
(new button%
[label "FRONT"]
[callback (lambda (aB anE)
(if frontal
(begin
(set! frontal #f)
(send aB set-label "BACK"))
(begin
(set! frontal #t)
(send aB set-label "FRONT"))))]
[parent difficultyBox]))
(define difficultyLabel
(new message%
[label "Difficulty:"]
[parent difficultyBox]))
(define buttonVec
(vector
(new button%
[label "ITYTD"]
[callback (curry change-difficulty 0)]
[enabled #t]
[parent difficultyBox])
(new button%
[label "HNTR"]
[callback (curry change-difficulty 1)]
[enabled #t]
[parent difficultyBox])
(new button%
[label "HMP"]
[callback (curry change-difficulty 2)]
[enabled #t]
[parent difficultyBox])
(new button%
[label "UV+"]
[callback (curry change-difficulty 3)]
[enabled #f]
[parent difficultyBox])))
(define fileName
(new text-field%
[style (list 'single)]
[label "Output:"]
[init-value "default.lua"]
[parent buttonBox]))
(define tehArea
(new text-field%
[style (list 'multiple)]
[font (make-object font% 14 'modern)]
[init-value (string-join (list "......................................................."
"..............,,,,,,,,,,,,,............................"
".....,,,,..,,,,,,,########,,,,,,,,,,,....,,,,,,,,,....."
".......,,,,,,,,,,,#,,,,,,#########,,,,,,,,,,,,,,,,,,,.."
"........,,,,,,,,,,#1,,,,,+,,,,,,,#3,,,,,,,,,,,,,,,,,,,."
"......,,,,,,,,,####+###,,#2,,,,,,#######,,,,,,,,,,,,,.."
".....,,,,,,,,,,#,,,,,,########,,,#,4,,*#,,,,,,,,,,,,..."
".....,,,,,,,,,,#1,,,,,+,,,PH>#,,,+,,,,,+,,,,,,,,,,,...."
".......,,,,,,,,#,,,,,,########,,,#,4,,*#,,,,,,,,,......"
".....,,,,,,,,,,####+###,,#2,,,,,,#######,,,,,,,,,,,,..."
"......,,,,,,,,,,,,#1,,,,,+,,,,,,,#3,,,,,,,,,,,,,,,,...."
"......,,,,,,,,,,,,#,,,,,,#########,,....,,,,,,,,,,,...."
"........,,,,,,,,,,########,,,,,,,......................"
"..........,,,,,,,,,,,,,,,,,,,,,........................"
"..............,,,,,.....,,,,...........................")
"\n")]
[label "Level:"]
[parent outerBox]))
(send tehFrame show #t)
Simply save the code to a .rkt file and run it with DrRacket. The interface should be pretty straight forward. There is a text area for the level, buttons for changing the difficulty level, a button for front/back toggling, a file name text area and a generate button. Simply paste in the level, select your options and hit generate.
Caveats:
DrRacket runs on Linux/OSX/Windows, but you'll need it installed because I was way too lazy to create/post binaries. You can get it here http://racket-lang.org/ (http://racket-lang.org/).
I wrote this under Linux but briefly tested it under a virtual Windows. It seems to work correctly. It was written with DrRacket v5.0.1 but tested with v5.1.1(current). They both seem to work.
I didn't do the greatest error checking in the world, there is a rough check to see if the map is the right size and it doesn't check the filename, it just assumes it is correct.
If you use any other symbol than the ones already defined, I'm not sure what will happen. It should still generate a playable level, but I never tried.
I think I lined up the map/player placement correctly, but I could be wrong.
Here is some sample output from the second map from General Patton's post:
Levels("CUSTOM",{
name = "Phobos Test",
mortem = "",
mortem_desc = "in testing",
Create = function ()
Level.fill("wall")
local translation = {
['.'] = "rock",
[','] = "floor",
['#'] = "wall",
['+'] = "door",
['>'] = "stairs",
['*'] = "floor",
['P'] = "floor",
['H'] = "floor",
['1'] = "floor",
['2'] = "floor",
['3'] = "floor",
['4'] = "floor"
}
local center = [[
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
]]
local map = [[.......................................................
..............,,,,,,,,,,,,,............................
.....,,,,..,,,,,,#########,,,,,,,,,,,....,,,,,,,,,.....
.......,,,,,,,,,,#*+,,,,,###3,,,,,,,,,,,,,,,,,,,,,,,,..
........,,,,,,,#######,,1+,###,,,,,,#,,,,,,,,,,,,,,,,,.
......,,,,,,,,,#,,,+,#,,,#2,,###,,,,###,,,,,,,,,,,,,,..
.....,,,,,,,,,,#,,,#,#####,,,,,####,,,#,,,,,,,,,,,,,...
.....,,,,,,,,,,#>HP#,,1,,+,,,4,,,+,,,4+,,,,,,,,,,,,....
.......,,,,,,,,#,,,#,#####,,,,,####,,,#,,,,,,,,,,......
.....,,,,,,,,,,#,,,+,#,,,#2,,###,,,,###,,,,,,,,,,,,,...
......,,,,,,,,,#######,,1+,###,,,,,,#,,,,,,,,,,,,,,....
......,,,,,,,,,,,#*+,,,,,###3,,,,,,,....,,,,,,,,,,,....
........,,,,,,,,,#########,,,,,,,,.....................
..........,,,,,,,,,,,,,,,,,,,,,........................
..............,,,,,.....,,,,...........................]]
Level.place_tile(translation, center, 2, 2 )
Level.place_tile(translation, map, 9, 3 )
Level.player(75,10)
Level.drop_item("smed", coord.new(27,6))
Level.drop_being("former", coord.new(37,6))
Level.drop_being("former", coord.new(33,7))
Level.drop_being("former", coord.new(35,8))
Level.drop_being("sergeant", coord.new(26,10))
Level.drop_item("garmor", coord.new(27,10))
Level.drop_being("former", coord.new(31,10))
Level.drop_being("sergeant", coord.new(38,10))
Level.drop_being("sergeant", coord.new(46,10))
Level.drop_being("former", coord.new(35,12))
Level.drop_being("former", coord.new(33,13))
Level.drop_item("smed", coord.new(27,14))
Level.drop_being("former", coord.new(37,14))
end,
OnEnter = function ()
player.eq.weapon = item.new("pistol")
end,
OnKill = function ()
end,
OnKillAll = function ()
end,
OnExit = function ()
levels.CUSTOM.mortem = "fled alive the trials"
end,
})
We don't have a circle, so here is a quick attempt at one.
.....................,,,,#####,,,,.....................
...................,,,####,,,####,,,...................
.................,,,####,,,,,,,,###,,,.................
...............,,,###,3#,,,,,,,,,,###,,,...............
..............,,###,,,,#2,,,,,,,,,,,###,,..............
.............,,##,,,,,#####+####,,,,,,##,,.............
.............,##,,,,,,#1,,,,,,,#4,,,,,*##,.............
.............,#>PH,,,,+,,,,,,,1#,,,,,,,,+,.............
.............,##,,,,,,#1,,,,,,,#4,,,,,*##,.............
.............,,##,,,,,#####+####,,,,,,##,,.............
..............,,###,,,3#2,,,,,,,,,,,###,,..............
...............,,,###,,#,,,,,,,,,,###,,,...............
.................,,,####,,,,,,,,###,,,.................
...................,,,####,,,####,,,...................
.....................,,,,#####,,,,.....................
UV Version:
Levels("CUSTOM",{
name = "Phobos Test",
mortem = "",
mortem_desc = "in testing",
Create = function ()
Level.fill("wall")
local translation = {
['.'] = "rock",
[','] = "floor",
['#'] = "wall",
['+'] = "door",
['>'] = "stairs",
['*'] = "floor",
['P'] = "floor",
['H'] = "floor",
['1'] = "floor",
['2'] = "floor",
['3'] = "floor",
['4'] = "floor"
}
local center = [[
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
]]
local map = [[.....................,,,,#####,,,,.....................
...................,,,####,,,####,,,...................
.................,,,####,,,,,,,,###,,,.................
...............,,,###,3#,,,,,,,,,,###,,,...............
..............,,###,,,,#2,,,,,,,,,,,###,,..............
.............,,##,,,,,#####+####,,,,,,##,,.............
.............,##,,,,,,#1,,,,,,,#4,,,,,*##,.............
.............,#>PH,,,,+,,,,,,,1#,,,,,,,,+,.............
.............,##,,,,,,#1,,,,,,,#4,,,,,*##,.............
.............,,##,,,,,#####+####,,,,,,##,,.............
..............,,###,,,3#2,,,,,,,,,,,###,,..............
...............,,,###,,#,,,,,,,,,,###,,,...............
.................,,,####,,,,,,,,###,,,.................
...................,,,####,,,####,,,...................
.....................,,,,#####,,,,.....................]]
Level.place_tile(translation, center, 2, 2 )
Level.place_tile(translation, map, 9, 3 )
Level.player(2,2)
Level.drop_being("former", coord.new(31,6))
Level.drop_being("former", coord.new(33,7))
Level.drop_being("former", coord.new(32,9))
Level.drop_being("sergeant", coord.new(41,9))
Level.drop_item("smed", coord.new(47,9))
Level.drop_item("garmor", coord.new(25,10))
Level.drop_being("sergeant", coord.new(26,10))
Level.drop_being("former", coord.new(39,10))
Level.drop_being("former", coord.new(32,11))
Level.drop_being("sergeant", coord.new(41,11))
Level.drop_item("smed", coord.new(47,11))
Level.drop_being("former", coord.new(31,13))
Level.drop_being("former", coord.new(33,13))
end,
OnEnter = function ()
player.eq.weapon = item.new("pistol")
end,
OnKill = function ()
end,
OnKillAll = function ()
end,
OnExit = function ()
levels.CUSTOM.mortem = "fled alive the trials"
end,
})
Edit: Swapped 2 and 3, so mobs clump less on lower difficulties.
AStranger, your idea of providing a sandbox mod to test these levels out is excellent. As much as I love scheme racket, I think it will be easier for most people to directly use a sandbox mod, so I took the liberty of hacking one up. The source is here. If you don't know what to do with a sandbox mod, download the attached package instead. It's readme.txt provides instructions.
-- Intro Layout Tester
-- by tehtmi
-- Inspired by AStranger
-- Use this to choose a difficulty level.
-- 1 : I'm Too Young To Die
-- 2 : Hey, Not Too Rough
-- 3 : Hurt Me Plenty
-- 4 : Ultra-Violence
DIFFICULTY = 4
-- Use this to select a challenge mode.
-- 0 : Standard Game
-- 1 : Angel of Berserk
-- 2 : Angel of Marksmanship
-- 3 : Angel of Shotgunnery
local challenge = 0
-- Optionally, uncomment the following line to override the
-- FRONTAL option from config.lua
-- 0 : start in the upper-left corner
-- 1 : start on the right-hand side
-- FRONTAL = 1
-- You can set the following option to true to reveal the entire level.
-- This might be nice if you want to get a feel for how it looks in-game
-- or how the enemies act before the player reaches them.
local reveal = false
-- If this option is enabled, the program will try to find errors in the
-- layout including bad dimensions, stray characters, or an improper amount
-- or one thing or another. (Experimental)
local verify = false
-- Replace the layout here with the one you want to test.
local layout = [[
.......................................................
..............,,,,,,,,,,,,,............................
.....,,,,..,,,,,,,########,,,,,,,,,,,....,,,,,,,,,.....
.......,,,,,,,,,,,#,,,,,,#########,,,,,,,,,,,,,,,,,,,..
........,,,,,,,,,,#1,,,,,+,,,,,,,#3,,,,,,,,,,,,,,,,,,,.
......,,,,,,,,,####+###,,#2,,,,,,#######,,,,,,,,,,,,,..
.....,,,,,,,,,,#,,,,,,########,,,#,4,,*#,,,,,,,,,,,,...
.....,,,,,,,,,,#1,,,,,+,,,PH>#,,,+,,,,,+,,,,,,,,,,,....
.......,,,,,,,,#,,,,,,########,,,#,4,,*#,,,,,,,,,......
.....,,,,,,,,,,####+###,,#2,,,,,,#######,,,,,,,,,,,,...
......,,,,,,,,,,,,#1,,,,,+,,,,,,,#3,,,,,,,,,,,,,,,,....
......,,,,,,,,,,,,#,,,,,,#########,,....,,,,,,,,,,,....
........,,,,,,,,,,########,,,,,,,......................
..........,,,,,,,,,,,,,,,,,,,,,........................
..............,,,,,.....,,,,...........................
]]
-----------------------------------------------------
-- Below here is stuff you don't have to worry about.
-----------------------------------------------------
-- Custom challenge constants.
local challenge_standard = 0
local challenge_berserk = 1
local challenge_marksman = 2
local challenge_shotgun = 3
-- List of errors to diplay in OnEnter.
local errors = {}
-- Verification for layouts.
local function verify_layout()
local set = {}
local len = layout:len()
if len ~= 840 then
table.insert(errors, "Bad length!")
return false
end
-- String referencing isn't working for me for some reason... so
-- string.sub to the rescue.
for i = 1, 840 do
-- Skip line breaks.
if i % 56 ~= 0 then
local ch = layout:sub(i, i)
if set[ch] then
set[ch] = set[ch] + 1
else
set[ch] = 1
end
end
end
local proper = {
["."] = 0, -- meaning any number
[","] = 0,
["#"] = 0,
["+"] = 0,
[">"] = -1, -- meaning at least 1
["P"] = 1,
["1"] = 3,
["2"] = 2,
["3"] = 2,
["4"] = 2,
["H"] = 1,
["*"] = 2,
}
for ch, num in pairs(set) do
local proper_count = proper[ch]
if proper_count == nil then
table.insert(errors, "Unexpected character: " .. ch)
return false
end
end
for ch, num in pairs(proper) do
if num == -1 and not set[ch] then
table.insert(errors, "Expected at least one of: " .. ch)
elseif num > 0 and set[ch] ~= num then
table.insert(errors, "Expected exactly " .. num .. " of " .. ch)
end
end
return true
end
-- Define the Phobos Base Entry level.
Levels("CUSTOM", {
name = "Phobos Base Entry",
entry = "He started his journey on the surface of Phobos.",
Create = function()
-- Handle the layout stuff.
Level.fill("pwall")
Level.fill("rock", area.FULL_SHRINKED)
local translation = {
["."] = "rock",
[","] = "floor",
["#"] = "wall",
["+"] = "door",
[">"] = "stairs",
["P"] = {"floor"},
["1"] = {"floor", being = "former"},
["2"] = {"floor"},
["3"] = {"floor"},
["4"] = {"floor"},
["H"] = {"floor", being = "sergeant"},
["*"] = {"floor", item = "smed"},
}
if challenge == challenge_berserk then
translation["P"].item = "knife"
elseif DIFFICULTY >= 4 then
translation["P"].item = "garmor"
end
if DIFFICULTY >= 2 then
translation["2"].being = "former"
end
if DIFFICULTY >= 3 then
translation["4"].being = "former"
end
if DIFFICULTY >= 4 then
translation["3"].being = "former"
translation["4"].being = "sergeant"
end
if not verify or verify_layout() then
Level.place_tile(translation, layout, 9, 3)
end
-- Checks the FRONTAL parameter from config.lua
if FRONTAL == 1 then
Level.player(75, 10)
else
Level.player(2, 2)
end
end,
OnEnter = function()
-- Report errors
for _, err in ipairs(errors) do
ui.msg("@rError: " .. err)
end
-- Handle the reveal option
if reveal then
Level.light[LFEXPLORED] = true
Level.flags[LF_BEINGSVISIBLE] = true
Level.flags[LF_ITEMSVISIBLE] = true
end
-- Set-up difficulty modifiers to accuracy
local toHit_mod = math.min(DIFFICULTY - 1, 3)
beings.former.toHit = beings.former.toHit + toHit_mod
beings.sergeant.toHit = beings.sergeant.toHit + toHit_mod
-- Set-up difficulty modifier to powerups
if DIFFICULTY == 1 or DIFFICULTY == 5 then
DIFF_MOD = 2
else
DIFF_MOD = 1
end
-- Set-up challenge-specific conditions.
if challenge == challenge_berserk then
-- Set-up special equipment
player.inv:clear()
player.eq:clear()
player.eq.armor = item.new("barmor")
player.inv:add("lmed")
player.inv:add("lmed")
elseif challenge == challenge_marksman then
-- Add the special mod.
player.inv:add(table.random_pick({
"mod_agility",
"mod_bulk",
"mod_tech",
}))
elseif challenge == challenge_shotgun then
-- Set-up special equipment
player.inv:clear()
player.eq:clear()
player.eq.weapon = item.new("shotgun")
player.inv:add("shell")
player.inv[1].ammo = 50
player.inv:add("smed")
player.inv:add("smed")
end
end,
})
-- Lua hooks must be changed before the level is created.
if challenge == challenge_berserk then
-- Set-up fire restrictions for pistol and shotgun
local OnFire = function(item)
if item.itype == ITEMTYPE_MELEE then
return false
end
ui.msg("You pull the trigger, but nothing happens. You're a berserker, dumbass!")
return true
end
items.pistol.OnFire = OnFire
items.shotgun.OnFire = OnFire
elseif challenge == challenge_marksman then
-- Set-up fire restriction for shotgun
local OnFire = function(item)
if item.flags[IF_PISTOL] then
return false
end
ui.msg("This weapon isn't worthy of a marksman!")
return true
end
items.shotgun.OnFire = OnFire
elseif challenge == challenge_shotgun then
-- Set-up fire restriction for pistol
local OnFire = function(item)
if item.flags[IF_SHOTGUN] then
return false
end
ui.msg("This is a weapon for wimps, not a true man!")
return true
end
items.pistol.OnFire = OnFire
end
I have made four layouts so far. Comments are welcome!
tehtmi 1
................,,,,......,,,,,,,,,,,,,,,,,............
...............,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,........
............,,,,##,,,,,,,,,,,,,,,,,,,,##,,,,,,,,.......
............,,,,####################+###,,,,,,,,.......
..............,,,#,,1,,,,,+,,,2,,#4,,,#,,,,,,..........
...........,,,,,,#,,,,#######,,,,#,,,*#,,,,,,,,,,......
............,,,,,#,,,,#,,,,,#,,,,#,,,,#,,,,,,,,,.......
.............,,,,#,,,,+1,H>P#,,,,+,,,,#,,,,,,,.........
...........,,,,,,#,,,,#,,,,,#,,,,#,,,,#,,,,,...........
.........,,,,,,,,#,,,,#######,,,,#,,,*#,,,.............
........,,,,,,,,,#,,1,,,,,+,,,2,,#4,,,#,,,,............
........,,,,,,,,####################+###,,,,,,.........
..........,,,,,,##,,,,,,,,,,,,,,,,,,,,##,,,,,,,........
............,,,,,,,,,,,,...,,,,..,3,,,,,,,,,...........
.............,,,,.,,,..............,,,,,...............
This is intended to be fairly straight-forward. Having the doors on the sides helps make both of the first two rooms areas of engagement.
tehtmi 2
.................,,,,,,,.......,,,,,,,,,,..............
.............,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.............
...........,,,,#######,,,,,,,,,####+##,,,,,,...........
............,,,#P,,,,##,,,,,,,##,,*,,#,,,..,,..........
.............,,#,>,,,###########,,,,,+,,,,.............
............,,,#,,H,1#,,,,#,,,,#4,,,*#,,,,,,...........
.........,,,,,,#,,,,,+,,,,#,,,,+,,,,,#,,,.,............
..........,,.,,#,,1,,#,,,,#,,,,#,,4,,#,,,..............
.............,,####+###,,,#,,,###+####,,,,.............
..............,,,#,,,##,,,#,,,##,,,#,3,,,,,,...........
............,,,,,#,,,,,,,,1,,,,,,,,#,,,,,..............
.............,,,,#,,,,2,,,,,,,2,,,,#,3,,...............
................,###################,,,,,..............
...............,,,,,..,,,,,,,...,,,,,,.................
.....................,,,.,,............................
Here, I was trying out a layout that wasn't vertically symmetric. The wall in the middle serves to break the combat into two major parts.
tehtmi 3
.......................................................
..............,,,......................................
...........##,,##########################.,,##.........
...........#,,,#,,,,,,#...........,#,,,,#,,,3#.........
............,,,#,,1,,,+,2.,......,,+,4,,##,,...........
...........#####,,,,,,##..........##,,,,*#,,,..........
...........#,,,###+####.,,.,,,...,,##,,,,#,,...,,,...,,
...........#,>,P,,,,H#,,##,1##,,##,,#,,,,+,,,...,,,....
...........#,,,###+####,,,,..,..,,,##,,,,#,............
..........,#####,,,,,,##..........##,,,,*#.............
..........,,,,,#,,1,,,+,2.........,+,4,,##.............
.........,,#,,,#,,,,,,#,,..........#,,,,#..,3#.........
........,,,##,,##########################.,,##.........
.........,,,,,.,,......................................
.......................................................
Here there is only one major battleground: the courtyard in the middle. The broken horizontal wall along the middle helps keep the enemies from engaging all at once.
tehtmi 4
.............................,,..,,...,................
...................,.,.,,,,,,,,,,,,,,,,,,..............
................,,,,,,,################,,,,,,..........
.............,,,########2,,,,,,,,,,,,,#,,,.............
............,,,,#,,+,1,#,,,,,,,,,,,,,,#,,..............
...........,,,,,#,,##,,#,,,##,,#4,,,*##,,,.............
..............,,#,,,#,,##,,,#,,#######3,,,,,,..........
.............,,,#HP>#,,+,,1,#,,+,,,,,,,,,,,,,,,........
..............,,#,,,#,,##,,,#,,#######3,,,,,,,.........
...........,,,,,#,,##,,#,,,##,,#4,,,*##,,,,,...........
............,,,,#,,+,1,#,,,,,,,,,,,,,,#,,,.............
.........,,,,,,,########2,,,,,,,,,,,,,#,,,,............
........,,,..,,,,,,,,,,################,..,............
................,,,,...,,.,,,,,,,,,,,,,,...............
................................,,,,,..................
This may look innocent, but it can be deadly if not approached carefully. There are few doors, and the sergeants are placed where they will cause a great deal of pain to an unsuspecting marine.
Deathwind: I like the way the layout can be approached from both sides. The long horizontal walls break up the level in an interesting way, but I agree with General Patton that such long hallways would be deadly to the player without any source of cover.
AStranger: The circle is simple, but it works. The middle room seems to do a good job of containing some of the enemies.
Shoop Da Woop: The gate is a cool idea. Also, the medpacks idea is interesting, but I'm not sure if any player would opt to pass them by.