Modding:Tutorial/The Infinite Arena/Source

From DoomRL Wiki

Jump to: navigation, search
core.declare("inf_arena", {} )
 
--enemy scaling for each wave: note that game difficulty will modify scaling independently
local mob_scale_factor = 0.5
--customize min_lev/max_lev/weight of each enemy (defaults given)
--for names of beings, check Object IDs in the modding documentation
--be aware that JC auto-ends the game unless modified directly
local mobGenStats = {
    { being = "former",         min_lev = 0,   max_lev = 12,  weight = 10 },
    { being = "sergeant",       min_lev = 2,   max_lev = 15,  weight = 10 },
    { being = "captain",        min_lev = 8,   max_lev = 15,  weight = 10 },
    { being = "imp",            min_lev = 0,   max_lev = 17,  weight = 8  },
    { being = "demon",          min_lev = 4,   max_lev = 20,  weight = 6  },
    { being = "lostsoul",       min_lev = 6,   max_lev = 16,  weight = 10 },
    { being = "knight",         min_lev = 9,   max_lev = 15,  weight = 6  },
    { being = "cacodemon",      min_lev = 10,  max_lev = 50,  weight = 6  },
    { being = "commando",       min_lev = 12,  max_lev = 17,  weight = 6  },
    { being = "pain",           min_lev = 12,  max_lev = 17,  weight = 2  },
    { being = "baron",          min_lev = 12,  max_lev = 200, weight = 6  },
    { being = "arachno",        min_lev = 13,  max_lev = 50,  weight = 4  },
    { being = "revenant",       min_lev = 13,  max_lev = 200, weight = 5  },
    { being = "mancubus",       min_lev = 15,  max_lev = 200, weight = 7  },
    { being = "arch",           min_lev = 16,  max_lev = 200, weight = 4  },
    { being = "nimp",           min_lev = 30,  max_lev = 60,  weight = 8  },
    { being = "ndemon",         min_lev = 40,  max_lev = 200, weight = 6  },
    { being = "ncacodemon",     min_lev = 51,  max_lev = 200, weight = 6  },
    { being = "narachno",       min_lev = 50,  max_lev = 200, weight = 5  },
    { being = "narch",          min_lev = 90,  max_lev = 200, weight = 3  },
    { being = "bruiser",        min_lev = 50,  max_lev = 200, weight = 6  },
    { being = "lava_elemental", min_lev = 70,  max_lev = 200, weight = 1  },
    { being = "shambler",       min_lev = 80,  max_lev = 200, weight = 3  },
    { being = "agony",          min_lev = 80,  max_lev = 200, weight = 1  },
    { being = "cyberdemon",     min_lev = 80,  max_lev = 200, weight = 1  },
    { being = "arenamaster",    min_lev = 0,   max_lev = 0,   weight = 0  },
    { being = "angel",          min_lev = 0,   max_lev = 0,   weight = 0  },
    { being = "jc",             min_lev = 0,   max_lev = 0,   weight = 0  },
}
 
--item scaling for each wave
local item_scale_factor = 0.5
--customize level/weight of items (defaults given)
--for names of items, check Object IDs in the modding documentation
local itemGenStats = {
    --Commons
 
    --weapons
    { item = "knife",        level = 1,  weight = 640 },
    { item = "pistol",       level = 1,  weight = 70  },
    { item = "shotgun",      level = 2,  weight = 180 },
    { item = "ashotgun",     level = 2,  weight = 160 },
    { item = "dshotgun",     level = 4,  weight = 100 },
    { item = "chaingun",     level = 5,  weight = 200 },
    { item = "bazooka",      level = 7,  weight = 200 },
    { item = "plasma",       level = 12, weight = 70  },
    --ammo/ammo packs
    { item = "ammo",         level = 1,  weight = 500 },
    { item = "pammo",        level = 3,  weight = 700 },
    { item = "shell",        level = 2,  weight = 400 },
    { item = "pshell",       level = 4,  weight = 200 },
    { item = "rocket",       level = 5,  weight = 60  },
    { item = "procket",      level = 7,  weight = 60  },
    { item = "cell",         level = 8,  weight = 36  },
    { item = "pcell",        level = 10, weight = 18  },
    --armor/boots
    { item = "garmor",       level = 1,  weight = 400 },
    { item = "barmor",       level = 4,  weight = 240 },
    { item = "rarmor",       level = 7,  weight = 150 },
    { item = "sboots",       level = 4,  weight = 240 },
    { item = "pboots",       level = 7,  weight = 150 },
    { item = "psboots",      level = 11, weight = 80  },
    --consumables
    { item = "smed",         level = 1,  weight = 600 },
    { item = "lmed",         level = 5,  weight = 400 },
    { item = "phase",        level = 5,  weight = 200 },
    { item = "hphase",       level = 7,  weight = 100 },
    { item = "epack",        level = 5,  weight = 100 },
    { item = "nuke",         level = 10, weight = 40  },
    { item = "lava_element", level = 23, weight = 0   },
    { item = "mod_power",    level = 7,  weight = 120 },
    { item = "mod_tech",     level = 6,  weight = 120 },
    { item = "mod_bulk",     level = 6,  weight = 120 },
    { item = "mod_agility",  level = 5,  weight = 120 },
    --powerups
    { item = "shglobe",      level = 1,  weight = 900 },
    { item = "lhglobe",      level = 6,  weight = 330 },
    { item = "scglobe",      level = 4,  weight = 150 },
    { item = "bpack",        level = 1,  weight = 200 },
    { item = "iglobe",       level = 7,  weight = 200 },
    { item = "msglobe",      level = 16, weight = 60  },
    { item = "map",          level = 1,  weight = 200 },
    { item = "pmap",         level = 1,  weight = 80  },
    { item = "ashard",       level = 5,  weight = 700 },
    { item = "backpack",     level = 7,  weight = 0   },
 
    --Exotics
 
    --weapons
    { item = "chainsaw",        level = 12, weight = 3  },
    { item = "ublaster",        level = 8,  weight = 2  },
    { item = "ucpistol",        level = 4,  weight = 6  },
    { item = "uashotgun",       level = 6,  weight = 6  },
    { item = "upshotgun",       level = 12, weight = 4  },
    { item = "udshotgun",       level = 10, weight = 5  },
    { item = "uminigun",        level = 10, weight = 6  },
    { item = "umbazooka",       level = 10, weight = 6  },
    { item = "unapalm",         level = 10, weight = 6  },
    { item = "ulaser",          level = 12, weight = 5  },
    { item = "unplasma",        level = 15, weight = 4  },
    { item = "utristar",        level = 12, weight = 4  },
    { item = "bfg9000",         level = 20, weight = 2  },
    { item = "unbfg9000",       level = 22, weight = 2  },
    { item = "utrans",          level = 14, weight = 3  },
    --armor/boots
    { item = "uoarmor",         level = 7,  weight = 4  },
    { item = "uparmor",         level = 10, weight = 6  },
    { item = "upboots",         level = 8,  weight = 6  },
    { item = "ugarmor",         level = 15, weight = 6  },
    { item = "ugboots",         level = 10, weight = 6  },
    { item = "umedarmor",       level = 5,  weight = 6  },
    { item = "uduelarmor",      level = 5,  weight = 6  },
    { item = "ubulletarmor",    level = 2,  weight = 4  },
    { item = "uballisticarmor", level = 2,  weight = 5  },
    { item = "ueshieldarmor",   level = 5,  weight = 3  },
    { item = "uplasmashield",   level = 10, weight = 3  },
    { item = "uenergyshield",   level = 8,  weight = 3  },
    { item = "ubalshield",      level = 6,  weight = 3  },
    { item = "uacidboots",      level = 8,  weight = 5  },
    --consumables
    { item = "uswpack",         level = 5,  weight = 10 },
    { item = "ubskull",         level = 5,  weight = 8  },
    { item = "ufskull",         level = 7,  weight = 8  },
    { item = "uhskull",         level = 9,  weight = 8  },
    { item = "umod_firestorm",  level = 10, weight = 4  },
    { item = "umod_sniper",     level = 10, weight = 4  },
    { item = "umod_nano",       level = 10, weight = 4  },
    { item = "umod_onyx",       level = 10, weight = 4  },
 
    --Uniques
 
    --weapons	
    { item = "ubutcher",     level = 1,  weight = 2 },
    { item = "spear",        level = 16, weight = 0 },
    { item = "uscythe",      level = 16, weight = 0 },
    { item = "udragon",      level = 16, weight = 1 },
    { item = "utrigun",      level = 8,  weight = 2 },
    { item = "ujackal",      level = 10, weight = 2 },
    { item = "uberetta",     level = 6,  weight = 3 },
    { item = "usjack",       level = 12, weight = 2 },
    { item = "urbazooka",    level = 12, weight = 2 },
    { item = "uacid",        level = 12, weight = 3 },
    { item = "urailgun",     level = 15, weight = 2 },
    { item = "ubfg10k",      level = 20, weight = 1 },
    --armor/boots
    { item = "umarmor",      level = 15, weight = 3 },
    { item = "ucarmor",      level = 10, weight = 2 },
    { item = "unarmor",      level = 10, weight = 3 },
    { item = "umedparmor",   level = 10, weight = 2 },
    { item = "ulavaarmor",   level = 12, weight = 2 },
    { item = "uenviroboots", level = 10, weight = 2 },
    { item = "ushieldarmor", level = 10, weight = 2 },
    { item = "uberarmor",    level = 10, weight = 1 },
    { item = "aarmor",       level = 22, weight = 0 },
    --consumables
    { item = "uhwpack",      level = 10, weight = 4 },
    { item = "umodstaff",    level = 15, weight = 4 },
    { item = "uarenastaff",  level = 4,  weight = 0 },
}
 
--changes the generation stats of enemies based on mobGenStats
for _,v in ipairs(mobGenStats) do
    beings[v.being].min_lev = v.min_lev
    beings[v.being].max_lev = v.max_lev
    beings[v.being].weight = v.weight
end
 
--changes the generation stats of items based on itemGenStats
for _,v in ipairs(itemGenStats) do
    items[v.item].level  = v.level
    items[v.item].weight = v.weight
end
 
--add your own announcer lines here
local anncrMsg = {
    --displays whenever player completes wave
    success = {
        {
        "The voice booms, \"Congratulations mortal!",
        "The voice booms, \"Impressive mortal!",
        "The voice booms, \"Most impressive.",
        "The voice booms, \"You are a formidable warrior!",
        },
        {
        "Each of your triumphs is a work of art!",
        "Your ability to survive is incredible!",
        "You've given us a great show!",
        "You would make a terrific hell warrior!",
        },
        {
        "But can you keep going?\"",
        "How much longer can you go?\"",
        "Will you fight with us a little more?\"",
        "I can let you go now if you like, or...\"",
        }
    },
    --displays whenever player decides to continue to another wave
    choice = {
        {
        "The voice booms, \"I like it! Let the show go on!\"",
        "The voice booms, \"Excellent! May the fight begin!!!\"",
        },
        {
        "You hear screams everywhere! \"More Blood! More BLOOD!\"",
        "You hear screams everywhere! \"Kill, Kill, KILL!\"",
        }
    }
}
 
--add your own crowd lines here
--displays whenever enemy is killed
local crowdMsg = {
    "The crowd goes wild! \"BLOOD! BLOOD!\"",
    "The crowd cheers! \"Blood! Blood!\"",
    "The crowd cheers! \"Kill! Kill!\"",
    "The crowd hisses. \"We came for a REAL fight!\"",
    "The crowd boos. \"No skill, no kill!\"",
}
 
local function build_announcerMsg(MSG)
    for _,msgNum in ipairs(MSG) do
        ui.msg(table.random_pick(msgNum))
    end
end
 
--from Skulltag Arena, credit goes to yaflhdztioxo
--initializes table of cells that contains only corpses
local corpseCells = {}
 
local function get_corpses()
    for i = 1, #cells do
        if cells[i] and cells[i].flags then
            if cells[i].flag_set[CF_CORPSE] == true then
                table.insert(corpseCells, i) --i == sID's numeric value
            end
        end
    end
end
 
--removes corpses and blood-like cells
local function clear_corpses()
    --fade away all blood
    Generator.transmute("blood", "floor")
    Generator.transmute("bloodpool", "blood")
    --changes corpses to blood
    for i = 1, #corpseCells do
        Generator.transmute(corpseCells[i], "bloodpool")
    end
end
 
--changes player equipment
--add "nil" for any slot that should be empty
--always refreshes all slots: do not use to change only one equipment slot
local function build_gear(weap,prep,body,foot)
    player.eq:clear()
    if weap then player.eq.weapon   = item.new(weap) end --main weapon
    if prep then player.eq.prepared = item.new(prep) end --prepared weapon	
    if body then player.eq.armor    = item.new(body) end --body armor
    if foot then player.eq.boots    = item.new(foot) end --boots
end
 
--changes player inventory
--always refreshes all slots: do not use to change only one equipment slot
--[[
input is an array of tables with two fields:
-name is the identifier of the item that goes in the inventory
-amt is how many items should be added (or how many units of ammo)
example table (based on default inventory):
 
local starter_pack = {
    {name = "ammo", amt = 24},
    {name = "smed", amt = 2},
}
--]]
local function build_pack(itemPack)
    player.inv:clear()
    for _,part in ipairs(itemPack) do
        if items[part.name].type == ITEMTYPE_AMMO then
            --ammo is handled separately, loops for however many stacks are needed
            for n=1,math.ceil(part.amt/items[part.name].ammomax) do
                local pack = item.new(part.name)
                if part.amt < pack.ammomax then
                    --remainder (or single) stack
                    pack.ammo = part.amt
                else
                    --filled (and/or multiple) stacks
                    pack.ammo = pack.ammomax
                    part.amt = part.amt - pack.ammomax
                end
                player.inv:add(pack)
            end
        else
            --loops for however many items are needed
            for n=1,part.amt do
                player.inv:add(part.name)
            end
        end
    end
end
 
 
 
function inf_arena.OnEnter()
    --inventory table used for build_pack()
    local itemPack = {
        {name = "smed",  amt = 2},
        {name = "shell", amt = 50},
    }
    --set up starting eq/inv
    build_gear("shotgun","pistol",nil,nil)
    build_pack(itemPack)
 
    --Print announcer messages
    ui.msg("A devilish voice announces: \"Welcome to Hell's Arena, mortal! " ..
           "\"You are either very brave or very foolish. Either way I like it! " ..
           "\"And so do the crowds!\" Suddenly you hear screams everywhere! " ..
           "\"Blood! Blood! BLOOD!\" \"Kill all enemies and I shall reward thee!\"")
 
    --spawn first wave
    Level.flood_monsters(Generator.being_weight()*mob_scale_factor)
    Level.result(1)
end
 
function inf_arena.OnKill(being)
    --random message from crowdMsg array
    ui.msg(table.random_pick(crowdMsg))
end
 
function inf_arena.OnKillAll()
    --print more announcer stuff
    if Level.result() == 1 then
        ui.msg("The voice booms, \"Not bad mortal! For a weakling that you are, " ..
               "you show some determination.\" You hear screams everywhere! " ..
               "\"More Blood! More BLOOD!\" The voice continues, \"I can now " ..
               "let you go free, or you may try to complete the challenge!\"")
    elseif Level.result() == 2 then
        ui.msg("The voice booms, \"Impressive mortal! Your determination to " ..
               "survive makes me excited!\" You hear screams everywhere! " ..
               "\"More Blood! More BLOOD!\" \"I can let you go now, and give you " ..
               "a small reward, or you can choose to fight an additional challenge!\"")
    else
        --random message from anncrMsg.success array
        build_announcerMsg(anncrMsg.success)
    end
 
    local choice = ui.msg_confirm("Round " .. Level.result() + 1 .. " awaits. " ..
                                  "Do you want to continue the fight?")
    if choice == true then --continuing
        --random message from anncrMsg.choice array
        build_announcerMsg(anncrMsg.choice)
        --set up the danger level for the next wave
        Level.result(Level.result() + 1);
        Level.danger_level = Level.result();
        --spawn items for next wave
        Level.flood_items(Generator.item_amount()*item_scale_factor)
        --spawn enemies for next wave
        Level.flood_monsters(Generator.being_weight()*mob_scale_factor)
 
    else --quitting
        if Level.result() == 1 then
            ui.msg("The voice booms, \"Coward!\" You hear screams everywhere! " ..
                   "\"Coward! Coward! COWARD!\"")
        elseif Level.result() == 2 then
            ui.msg("The voice booms, \"Too bad, you won't make it far then...!\" " ..
                   "You hear screams everywhere! \"Boooo...\"")
        elseif Level.result() < 10 then
            ui.msg("The voice booms, \"An impressive run, Mortal!  We appreciate " ..
                   "it!\" The crowd starts to chant! \"Encore! Encore!\"")
        else
            ui.msg("\"Ladies and gentlemen, your champion, "
                   .. Player.get_name() .. ". He survived " .. Level.result() ..
                   " rounds in our arena! That has to be some sort of record. " ..
                   "Give him a hand folks!\" The crowd starts to chant your name " ..
                   "and they begin throwing items into the ring!")
            Level.flood_items(Generator.item_weight())
        end
    end
end
 
function inf_arena.OnExit()
    if Level.result() < 10 then
        ui.msg("The voice laughs, \"Flee mortal, flee! There's no hiding in hell!\"")
    else
        ui.msg("The voice laughs, \"Remember to come back once you return to Hell " ..
               "for the extended stay\"")
    end
    --used in .OnMortem()
    inf_arena.result = "had enough of the gauntlet at wave "..Level.result()
end
 
function inf_arena.OnMortem()
    local kill = player.killedby --calls kill descriptions from beings
    if inf_arena.result then kill = inf_arena.result end
        player:mortem_print( " "..player.name..", level "..player.explevel.." "
                             .." "..klasses[player.klass].name..", "..kill )
        --e.g., "Cool Guy, level 1 Marine, had enough of the gauntlet at wave 8"
        player:mortem_print(" in the Infinite Arena...")
end
 
--Creation of Infinite Arena
function inf_arena.run()
    Level.name = "Infinite Arena"
    Level.name_number = 0
    Level.fill("rwall")
 
    local translation = {
    ["."] = "floor",
    ["#"] = {"rwall", LFPERMANENT = true},
    ["X"] = "rwall",
    [","] = "blood",
    [">"] = "stairs",
    }
 
    local map = [[
#######################.............................########################
###########.....................................................############
#####..................................................................#####
##........................................................................##
#..........................................................................#
....,.......................................................................
.................................,...,......................................
.,.....................................,....................................
..,>.,..........................,...........................................
.,..,.................................,.,...................................
................................,..,...,....................................
............................................................................
............................................................................
#..........................................................................#
##........................................................................##
#####..................................................................#####
###########.....................................................############
#######################.............................########################
]]
    --change up the column types
    local column = {
    [[
,..,.,
,XXXX.
.X##X,
.XXXX.
,..,.,
    ]],
    [[
,..,.,
,X##X.
.####,
.X##X.
,..,.,
    ]],
    [[
,..,.,
,####.
.####,
.####.
,..,.,
    ]]
    }
 
    Level.place_tile(translation,map,2,2)
    for i=1,11 + math.random(4) do
        Level.scatter_put( area.new(5,3,68,15), translation, table.random_pick(column), "floor", 1)
    end
    Level.scatter(area.FULL_SHRINKED, "floor", "blood", 100)
    Level.player(38, 10)
end
Personal tools