Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Text levels? #308

Closed
agiantwhale opened this issue Apr 25, 2018 · 9 comments
Closed

Text levels? #308

agiantwhale opened this issue Apr 25, 2018 · 9 comments

Comments

@agiantwhale
Copy link

Hi,

Are there any plans to add text level feature like in DeepMind Lab? It would simply map generation process for those working on navigation problems.

Thanks,
Jae

@mwydmuch
Copy link
Member

Hello @agiantwhale,
we don't plan it, if there is a need to prepare maps, nice and easy to use editors are available.
If you want to generate maps automatically, you can use this nice library https://github.com/devinacker/omgifol to generate new or manipulate existing Doom wads from Python.

@mwydmuch
Copy link
Member

mwydmuch commented Apr 26, 2018

However, if you, @agiantwhale or some other ViZDoom user will prepare (or already have) such a script, we will be happy to accept a pull request or put a link to it in README.md :)

@agiantwhale
Copy link
Author

agiantwhale commented Apr 26, 2018

I am taking a stab at it, but it's quite a difficult task. I will submit a PR if I get something usable working.

@agiantwhale
Copy link
Author

agiantwhale commented Apr 26, 2018

I have something working, but there are some issues with floor and ceiling flat textures. Could someone more experienced in Doom programming take a look?

I believe the issue is with Sector() flags.

Screenshot:
Screenshot 1
Screenshot 2

Sample level:

XXXXXXXXX
X       X
X XXXXX X
X X   X X
X XXX X X
X   X   X
X XXXXX X
X       X
XXXXXXXXX

Script (execute as ./levels.py maze.txt maze.wad):

from __future__ import print_function
from omg.txdef import *
from omg import *
from PIL import Image
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('maze')
parser.add_argument('wad')

BLOCK_SIZE = 128


def build_wall(maze):
    things = []
    linedefs = []
    vertexes = []
    v_indexes = {}

    max_w = len(maze[0].strip()) - 1
    max_h = len(maze) - 1

    def __is_edge(w, h):
        return w in (0, max_w) or h in (0, max_h)

    def __add_start(w, h):
        if __is_edge(w, h):
            return

        x, y = w * BLOCK_SIZE, h * BLOCK_SIZE
        x += int(BLOCK_SIZE / 2)
        y += int(BLOCK_SIZE / 2)
        things.append(Thing(*[x, y, 0, 1, 7]))

    def __add_vertex(w, h):
        if (w, h) in v_indexes:
            return

        x, y = w * BLOCK_SIZE, h * BLOCK_SIZE
        x += int(BLOCK_SIZE / 2)
        y += int(BLOCK_SIZE / 2)
        v_indexes[w, h] = len(vertexes)
        vertexes.append(Vertex(x, y))

    def __add_line(start, end, edge=False):
        assert start in v_indexes
        assert end in v_indexes

        mask = 1
        left = right = 0
        if __is_edge(*start) and __is_edge(*end):
            if not edge:
                return
            else:
                left = 1
                mask = 15

        line_properties = [v_indexes[start], v_indexes[end], mask] + [0] * 2 + [left, right]
        line = Linedef(*line_properties)
        linedefs.append(line)

    for h, row in enumerate(maze):
        for w, block in enumerate(row.strip()):
            if block == 'X':
                __add_vertex(w, h)
            else:
                pass

    corners = [(0, 0), (max_w, 0),
               (max_w, max_h), (0, max_h)]
    for v in corners:
        __add_vertex(*v)

    for i in range(len(corners)):
        if i != len(corners) - 1:
            __add_line(corners[i], corners[i + 1], True)
        else:
            __add_line(corners[i], corners[0], True)

    # Now connect the walls
    for h, row in enumerate(maze):
        for w, _ in enumerate(row):
            if (w, h) not in v_indexes:
                __add_start(w, h)
                continue

            grid_line = False

            # Prefer perpendicular connections first
            if (w + 1, h) in v_indexes:
                __add_line((w, h), (w + 1, h))
                grid_line = True
            if (w, h + 1) in v_indexes:
                __add_line((w, h), (w, h + 1))
                grid_line = True
            if (w - 1, h) in v_indexes:
                grid_line = True
            if (w, h - 1) in v_indexes:
                grid_line = True

    return things, vertexes, linedefs


def main(flags):
    new_wad = WAD()

    # img = Image.open('content/deepmind_wall.tga').resize((64, 64))
    # wall_lump = Graphic()
    # wall_lump.from_Image(img)
    # new_wad.patches['DMWALL'] = wall_lump
    #
    # floor_lump = Flat()
    # floor_lump.from_Image(img)
    # new_wad.flats['DMFLOOR'] = floor_lump

    new_map = MapEditor()
    with open(flags.maze) as maze_source:
        maze = maze_source.readlines()
    things, vertexes, linedefs = build_wall(maze)
    new_map.things = [Thing(128 + 64, 128 + 64, 0, 1, 7)]
    new_map.vertexes = vertexes
    new_map.linedefs = linedefs
    new_map.sectors = [Sector(0, 128, 'CEIL5_2', 'CEIL5_2', 240, 0, 0)]
    new_map.sidedefs = [Sidedef(0, 0, '-', '-', 'STONE2', 0),
                        Sidedef(0, 0, '-', '-', '-', 0)]
    new_wad.maps['MAP01'] = new_map.to_lumps()
    new_wad.to_file(flags.wad)


if __name__ == "__main__":
    main(parser.parse_args())

@Miffyli
Copy link
Collaborator

Miffyli commented Apr 26, 2018

@agiantwhale

First off: Brilliant job! I was planning to wrestle with this too, but seems like you were way ahead of me ^^.

I managed to find fix for your problem:

  • Apparently the outlines (linedefs) of map need to be one-sided lines (i.e. one of the sidedefs is -1 / 65535)
  • linedefs outlining the map need to "point inside" (right sidedef will be the void/-1/65535 one)

You can get your map working by replacing the __add_line function with following:

def __add_line(start, end, edge=False):
    assert start in v_indexes
    assert end in v_indexes

    mask = 1
    left = right = 0
    if __is_edge(*start) and __is_edge(*end):
        if not edge:
            return
        else:
            # Changed the right side (one towards outside the map)
            # to be -1 (65535 for Doom)
            right = 65535
            mask = 15
    
    # Flipped end and start vertices to make lines "point" at right direction (mostly to see if it works)
    line_properties = [v_indexes[end], v_indexes[start], mask] + [0] * 2 + [left , right]
    line = Linedef(*line_properties)
    linedefs.append(line)

Feel free to bug me with questions related to this project. I'd love to help get this code grow into practical thing!

@agiantwhale
Copy link
Author

Thank you for the fix!

I am planning to add ACC script functionality to the hacky script. This will be helpful for setting custom rewards for running into walls / respawn on arrival at goal. I will post again once I have it working, and then maybe I can work on cleaning the code up.

@agiantwhale
Copy link
Author

I've worked on something crude and is currently using it:

https://github.com/agiantwhale/navdoom

@mwydmuch
Copy link
Member

Awesome work guys!
I linked to it in our README.md and FAQ.md. Would be great to add README.md also to NavDoom.

@Miffyli
Copy link
Collaborator

Miffyli commented Sep 5, 2019

Note for anybody looking this in future: Check MazeExplorer

@Miffyli Miffyli closed this as completed Sep 5, 2019
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

3 participants