Author Topic: Generating special and boss levels  (Read 607 times)

Simon-v

  • Posts: 2
    • Personal website
Generating special and boss levels
« on: June 23, 2017, 12:56:35 AM »
Good day to all¸

I tend to find the boss levels included in Oblige repetitive. More variation would be welcome, but it's a lot of work to be done, for too little gain. Instead, i envisioned abandoning pre-built boss levels altogether, and having special, secret and boss levels generated by a normal, but slightly modified set of rules.

Example 1: Map 30

Use the normal generation algorithm. Use the "crazy" monster toughness setting. Assign higher weights to outdoor areas. Optionally, add a monster spawner somewhere on the level and monster teleport spots all over the place. Have the level end by a crusher switch. Result: Map 30 is fun to play, and is quite obviously the end-game.

Example 2: Secret maps 31/32

Use the normal generation algorithm. Use a higher monster count setting than normal (optionally, lock it to "nuts"). Use a higher monster strength setting than normal. Spawn a BFG9000 right in the starting room. Result: Maps that play very close to Plutonia's.

Example 3: Map 07

Use the normal generation algorithm. Force map size to "small". Use mancubi exclusively in the first half of the map. Use arachnotrons exclusively in the second half of the map. Result: Basically, a dead simple Dead Simple.


My question, therefore, is as follows:

I haven't had the opportunity to study Oblige's scripts yet. What changes need to be introduced to them to have the described effect? Where do i need to look? In other words, where would the "if map_number == 30 then ..." go?

andrewj

  • Developer
  • *****
  • Posts: 1512
Re: Generating special and boss levels
« Reply #1 on: June 24, 2017, 04:26:00 AM »
Your ideas for those special levels are quite good (perhaps not to my personal taste though).

They won't be trivial to implement though, but I can give some advice on what code to look at.

Firstly, special levels can be marked in the file games/doom/levels.lua in the DOOM.get_levels() function.  That is were prebuilt maps are handled (well partly).

The secret maps are already marked using "is_secret" field by that code.  I suggest starting with these for your experiments.

So to force monster amount to high in secrets, look in file scripts/monster.lua there is a function called calc_quantity().  What you can do is add a line right at the start which checks for a secret level and returns a high number.  For example:

Code: [Select]
local function calc_quantity()
    --
    -- result is a percentage (how many spots to use)
    --
    if LEVEL.is_secret then return 100 end

Forcing a BFG9000 in the start room is harder.  Hmmm.... you could do it by editing the visit_start_rooms() function in scripts/quest.lua and adding a line at the start:

Code: [Select]
local function visit_start_rooms()
  if LEVEL.is_secret then table.insert(LEVEL.start_room.weapons, "bfg") end

Play with that and see how you go.

Simon-v

  • Posts: 2
    • Personal website
Re: Generating special and boss levels
« Reply #2 on: July 07, 2017, 11:33:47 PM »
Thank you for the hints.

Seeking proper encapsulation, i was able to figure out the basics of module-making. Among other things, i found it possible to modify the VM state via hooks, one of which i immediately put to use, disabling the prebuilt map 30, to allow Oblige to generate it normally.

As i discovered, Oblige's default generation algorithms are quite good, and only ever need slight nudging. Here, i need clarifications.

I collected a list of hooks referenced in other modules. Are there any others available, but unused? When do they trigger? What data is available to them? In fact, i have to admit that, at present, the VM state at any given point is mostly a black box to me. I would, for example, like to:

1. Nudge the amount of monsters slightly up for a specific level. I trust the existing engine to handle the strength for me;

2.1. Increase the number of boss encounters for a specific level AND increase their variety; OR

2.2. Manually specify a set of boss encounters for a level and make sure all of them are placed.

A command line flag that would cause Oblige to generate only the desired levels would also be helpful: waiting for the full 32-level wad to finish generating is rather tiresome.

andrewj

  • Developer
  • *****
  • Posts: 1512
Re: Generating special and boss levels
« Reply #3 on: July 08, 2017, 06:58:19 PM »
The hooks are quite limited in what they can do.  Three main ones are:

(1) the "setup" hook which is called very early on (and once only).
(2) the "get_levels" hook, called later on (but once only)
(3) the "begin_level" hook is called before building each map

The hooks will modify global state, e.g. begin_level hooks often change things in the "LEVEL" table (information about the current level being generated).  Another example, the setup hooks in the Monster Control modules will modify the GAME.MONSTERS information (which is safe to modify since it is a copy of the original tables).

The hook system is too limited to do what you asked.  Some of the existing hook functions only work because the main scripts have code which checks for the changes they make.

If you want to change what boss monsters are used on a level, you need to look at code in scripts/levels.lua, especially the decide_boss_fights() function, e.g. add code which creates a FIGHT table explicitly for a specific monster.

Lastly, you can build only a single map by adding this on the command line:
Code: [Select]
only=MAP30

kop9000

  • Posts: 137
Re: Generating special and boss levels
« Reply #4 on: August 01, 2017, 08:32:03 AM »
.Lastly, you can build only a single map by adding this on the command line:
Code: [Select]
only=MAP30

By the way, this command is also needed for my project. Thank you.

And it is possible for example to generate by means of a command "only" not one level and we will tell three for example MAP02, MAP05, MAP15. If so how is this done?


kop9000

  • Posts: 137
Re: Generating special and boss levels
« Reply #5 on: August 01, 2017, 10:45:58 AM »
and another question. For some reason this "only" command does not work until the version of oblige 7.37. Ie he does not want to create eg only map02. It creates the first at once.

Why this command does not work up to the version of oblige 7.37?

andrewj

  • Developer
  • *****
  • Posts: 1512
Re: Generating special and boss levels
« Reply #6 on: August 01, 2017, 11:32:30 PM »
Why this command does not work up to the version of oblige 7.37?
I added the "only" keyword in version 5.07

So it should work in version 5.10, 6.05, 6.10, 6.20, and all V7 versions except 7.24

I just tested version 6.20 and it works there.

kop9000

  • Posts: 137
Re: Generating special and boss levels
« Reply #7 on: August 02, 2017, 07:45:23 AM »
Why this command does not work up to the version of oblige 7.37?
I added the "only" keyword in version 5.07

So it should work in version 5.10, 6.05, 6.10, 6.20, and all V7 versions except 7.24

I just tested version 6.20 and it works there.

Unfortunately, the "only" function in oblige 6.20 does not work for me. My team is like this

Code: [Select]
OBLIGE --batch rand.wad only = map02
And it does not work. No file is created

There is still a question whether I can add this function "only" in the oblige 3.51 and higher?

andrewj

  • Developer
  • *****
  • Posts: 1512
Re: Generating special and boss levels
« Reply #8 on: August 02, 2017, 10:51:50 PM »
There must not be any spaces between "open" and "=" and the map name.

For example:
Code: [Select]
OBLIGE --batch rand.wad only=map02
Quote
There is still a question whether I can add this function "only" in the oblige 3.51 and higher?
I am not sure if it will work.

In V7, the Level_make_level() function in scripts/levels.lua there is this code:
Code: [Select]
  -- debugging aid : ability to build only a particular level
  if OB_CONFIG.only and
     string.lower(OB_CONFIG.only) != string.lower(LEV.name)
  then
    gui.printf("\nSkipping level: %s....\n\n", LEV.name)
    return
  end

If you put that code at the start in the Level_make() function in the 3.57 code, rename LEV to L, then it might work.

kop9000

  • Posts: 137
Re: Generating special and boss levels
« Reply #9 on: August 03, 2017, 08:07:52 AM »
Quote
There must not be any spaces between "open" and "=" and the map name.

I do not have spaces, I threw you the same team as you. Just apparently he made the spaces himself when I copied the text. In any case I have no spaces

And still do not work at at oblige 6.20. only work at oblige 7.37 and above. help me please

kop9000

  • Posts: 137
Re: Generating special and boss levels
« Reply #10 on: August 03, 2017, 08:24:56 AM »
Quote
In V7, the Level_make_level() function in scripts/levels.lua there is this code:

It looks like it does not work in 3.51 or 3.57 like this I did Level_make ()

Code: [Select]
function Level_make(L, index, NUM)
  -- debugging aid : ability to build only a particular level
  if OB_CONFIG.only and
     string.lower(OB_CONFIG.only) != string.lower(L.name)
  then
    gui.printf("\nSkipping level: %s....\n\n", L.name)
    return
  end
  LEVEL = L

  assert(LEVEL)
  assert(LEVEL.name)

  gui.at_level(LEVEL.name, index, NUM)

  gui.printf("\n\n~~~~~~| %s |~~~~~~\n", LEVEL.name)

  LEVEL.seed = OB_CONFIG.seed * 100 + index

  THEME = shallow_copy(assert(LEVEL.sub_theme))

  if GAME.sub_defaults then
    merge_missing(THEME, GAME.sub_defaults)
  end


  -- use a pre-built level ?

  if LEVEL.prebuilt then
    Game_invoke_hook("begin_level_func",  LEVEL.seed)
    Game_invoke_hook("begin_level2_func", LEVEL.seed)

    local res = Level_handle_prebuilt()
    if res == "abort" then
      return res
    end

    Game_invoke_hook("end_level_func",  LEVEL.seed)
    Game_invoke_hook("end_level2_func", LEVEL.seed)

    return "ok"
  end


  gui.begin_level()
  gui.property("level_name", LEVEL.name);

  Level_styles()

  Game_invoke_hook("begin_level_func",  LEVEL.seed)
  Game_invoke_hook("begin_level2_func", LEVEL.seed)

  gui.printf("\nStyles = \n%s\n\n", table_to_str(STYLE, 1))


  local error_mat = assert(GAME.materials["_ERROR"])

  gui.property("error_tex",  error_mat.t)
  gui.property("error_flat", error_mat.f or error_mat.t)

  if LEVEL.description then
    gui.property("description", LEVEL.description)
  end


  local res = Level_build_it()
  if res == "abort" then
    return res
  end


  Game_invoke_hook("end_level_func",  LEVEL.seed)
  Game_invoke_hook("end_level2_func", LEVEL.seed)

  gui.end_level()


  -- intra-level cleanup
  if index < NUM then
    LEVEL = nil
    SEEDS = nil

    collectgarbage("collect")
  end

  return "ok"
end

Or so

Code: [Select]
function Level_make(L, index, NUM)
  LEVEL = L

  assert(LEVEL)
  assert(LEVEL.name)

  -- debugging aid : ability to build only a particular level
  if OB_CONFIG.only and
     string.lower(OB_CONFIG.only) != string.lower(L.name)
  then
    gui.printf("\nSkipping level: %s....\n\n", L.name)
    return
  end

  gui.at_level(LEVEL.name, index, NUM)

  gui.printf("\n\n~~~~~~| %s |~~~~~~\n", LEVEL.name)

  LEVEL.seed = OB_CONFIG.seed * 100 + index

  THEME = shallow_copy(assert(LEVEL.sub_theme))

  if GAME.sub_defaults then
    merge_missing(THEME, GAME.sub_defaults)
  end


  -- use a pre-built level ?

  if LEVEL.prebuilt then
    Game_invoke_hook("begin_level_func",  LEVEL.seed)
    Game_invoke_hook("begin_level2_func", LEVEL.seed)

    local res = Level_handle_prebuilt()
    if res == "abort" then
      return res
    end

    Game_invoke_hook("end_level_func",  LEVEL.seed)
    Game_invoke_hook("end_level2_func", LEVEL.seed)

    return "ok"
  end


  gui.begin_level()
  gui.property("level_name", LEVEL.name);

  Level_styles()

  Game_invoke_hook("begin_level_func",  LEVEL.seed)
  Game_invoke_hook("begin_level2_func", LEVEL.seed)

  gui.printf("\nStyles = \n%s\n\n", table_to_str(STYLE, 1))


  local error_mat = assert(GAME.materials["_ERROR"])

  gui.property("error_tex",  error_mat.t)
  gui.property("error_flat", error_mat.f or error_mat.t)

  if LEVEL.description then
    gui.property("description", LEVEL.description)
  end


  local res = Level_build_it()
  if res == "abort" then
    return res
  end


  Game_invoke_hook("end_level_func",  LEVEL.seed)
  Game_invoke_hook("end_level2_func", LEVEL.seed)

  gui.end_level()


  -- intra-level cleanup
  if index < NUM then
    LEVEL = nil
    SEEDS = nil

    collectgarbage("collect")
  end

  return "ok"
end

Both ways do not work

And when start the graphical shell of the oblige, there's such an error
« Last Edit: August 03, 2017, 09:47:42 AM by kop9000 »

kop9000

  • Posts: 137
Re: Generating special and boss levels
« Reply #11 on: August 03, 2017, 10:20:16 AM »
Additions to the post #9. I have Earned the comand "only" team. Just need to use extra comand

Length=

By the way, if you take for example, map02 then it is necessary to use length=few or length=episode or length=game

Although nachimnaya with the version of oblige 7.37 "only" works and without "length" apparently immediately takes length=few strange why

By the way, I checked the command "only" on the version of the oblige 4.28 and it also works there. WORK!!! WOW. Remained only the version of the oblige 3.51 and oblige.3.57

Only two versions remained. Please help in these two versions add the function "only".
« Last Edit: August 03, 2017, 10:56:04 AM by kop9000 »

andrewj

  • Developer
  • *****
  • Posts: 1512
Re: Generating special and boss levels
« Reply #12 on: August 03, 2017, 08:14:14 PM »
It looks like it does not work in 3.51 or 3.57 like this I did Level_make ()
Change "!=" to "~="

Some notes:

Tthe "only" keyword was meant for debugging, not other purposes.  What it really means is "skip all other levels".  Therefore it only makes sense when that level would normally be built, like when length is a full game.

In older versions, default length was "single" -- a single map.  The default changed to be a full game in V7 versions.

I suggest you always specify the length, use "full" for a full game as this keyword should work in all versions.

kop9000

  • Posts: 137
Re: Generating special and boss levels
« Reply #13 on: August 03, 2017, 09:26:02 PM »
andrewj

thank you very much. you are super.

I still have a small problem

I do not have enough information on the seed, namely the maximum number of seed, namely, these versions, since I could count not correctly

Oblige 3.51
Oblige 3.57
Oblige 4.28

Write please the maximum number of seed of these three versions

On all other versions oblige information about seed I have

andrewj

  • Developer
  • *****
  • Posts: 1512
Re: Generating special and boss levels
« Reply #14 on: August 04, 2017, 04:13:11 AM »
Oblige 3.51
Oblige 3.57
Oblige 4.28

Write please the maximum number of seed of these three versions

They all accept 31 bits for the seed, so usable range is from 0 to 2147483647