You Can't See Through Walls (Cosplore3D Pt:10)
Tags: go, gamedev, learning
Published: 2024-02-12 01:44:01

## Intro
This is a series following [Cosplore3D](https://github.com/Chig-Beef/Cosplore3D), a raycaster game to learn 3D graphics. This project is part of 12 Months 12 Projects, a challenge I set myself.

## Finding Out If Lines Intersect
All the information online I found about lines intersecting was always _slightly_ confusing, as in, most of it would make sense, and then there will be one line that is just out of left field. And this isn't just in one example, this is pretty much every single one. I don't want to have an equation like this in my code and not even understand it, so I decided I would ask Chat GPT. I forgot how good Chat GPT was at this stuff, but it gave me some nice Python code (and even translated it to Go for me). Let's have a look over the code it gave me (modified to fit my program).
```golang
func line_interset(x1, y1, x2, y2, x3, y3, x4, y4 float64) bool {
    // Calculate the slopes
    m1 := (y2 - y1) / (x2 - x1)
    m2 := (y4 - y3) / (x4 - x3)

    // Parallel (or close to apparently?)
    if math.Abs(m1-m2) < 1e-6 {
        return false
    }

    // Int as in intersection, not integer idiot
    var xInt, yInt float64
    if math.IsInf(m1, 0) {
        xInt = x1
        yInt = m2(x1-x3) + y3
    } else if math.IsInf(m2, 0) {
        xInt = x3
        yInt = m1(x3-x1) + y1
    } else {
        xInt = (m1x1 - m2x3 + y3 - y1) / (m1 - m2)
        yInt = m1*(xInt-x1) + y1
    }

    // Massive check
    return math.Min(x1, x2) <= xInt && xInt <= math.Max(x1, x2) &&
        math.Min(y1, y2) <= yInt && yInt <= math.Max(y1, y2) &&
        math.Min(x3, x4) <= xInt && xInt <= math.Max(x3, x4) &&
        math.Min(y3, y4) <= yInt && yInt <= math.Max(y3, y4)
}
```
Now we just need to check this with each of the 4 lines of a `Tile`.
```golang
func (t *Tile) check_line_in_tile(x1, y1, x2, y2 float64) bool {
    x3, y3 := t.x, t.y
    x4, y4 := t.x, t.y+t.h

    for x := 0; x < 2; x++ {
        for y := 0; y < 2; y++ {
            if line_interset(x1, y1, x2, y2, x3, y3, x4, y4) {
                return true
            }
            x4 += t.w
            y4 -= t.h
        }
        x3 += t.w
        y3 += t.h
        x4, y4 = t.x, t.y+t.h
    }

    return false
}
```
The way I've done this creates a weird checking order, but the order doesn't really matter, as long as all 4 sides are being checked.

## Ending See Through Walls
Now we need to simply call this function whenever we are trying to see an enemy or something similar.
```golang
for i := 0; i < len(tiles); i++ {
    if tiles[i].check_line_in_tile(e.x, e.y, c.x, c.y) {
        // Can't see through a wall
        return
    }
}
```
This works really when things are in a different position across the x coordinate, but poorly in the y coordinate. Turns out Chat GPT gave me some stinky code, I don't know why, but instead I used [this wiki article](https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection) and wrote this code.
```golang
func line_interset(x1, y1, x2, y2, x3, y3, x4, y4 float64) bool {
    t := ((x1-x3)*(y3-y4) - (y1-y3)*(x3-x4)) / ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    u := -((x1-x2)*(y1-y3) - (y1-y2)*(x1-x3)) / ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))

    if 0 <= t && t <= 1 && 0 <= u && u <= 1 {
        return true
    }

    return false
}
```
And it works perfectly! And I also implemented this logic for seeing items, and shooting.

## Next
These levels have been quite annoying to make, so in the next post we should start making an actual level editor. This level editor will allow us to make a 2D layout of a world, and edit the level's sky and floor color.