Skip to content

Commit

Permalink
Merge pull request #2 from moltenwolfcub/river
Browse files Browse the repository at this point in the history
Fixed River Jumping
  • Loading branch information
moltenwolfcub authored Jul 8, 2023
2 parents d0a0403 + c9e42e9 commit 649f080
Showing 1 changed file with 133 additions and 16 deletions.
149 changes: 133 additions & 16 deletions game/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
_ "embed"
"image"
_ "image/png"
"math"

"github.com/hajimehoshi/ebiten/v2"
"github.com/moltenwolfcub/Forest-Game/assets"
Expand Down Expand Up @@ -57,15 +58,17 @@ func (p Player) DrawAt(screen *ebiten.Image, pos image.Point) {
func (p Player) Overlaps(layer GameContext, other []image.Rectangle) bool {
return DefaultHitboxOverlaps(layer, p, other)
}
func (p Player) Origin(GameContext) image.Point {
return p.hitbox.Min
func (p Player) Origin(layer GameContext) image.Point {
bounds := p.findBounds(layer)
return bounds.Min
}
func (p Player) Size(GameContext) image.Point {
return p.hitbox.Size()
func (p Player) Size(layer GameContext) image.Point {
bounds := p.findBounds(layer)
return bounds.Size()
}
func (p Player) GetHitbox(layer GameContext) []image.Rectangle {
switch layer {
case Collision:
case Collision, Interaction:
baseSize := p.hitbox.Size().Y / 2

playerRect := image.Rectangle{
Expand All @@ -82,6 +85,19 @@ func (p Player) GetHitbox(layer GameContext) []image.Rectangle {
}
}

func (p Player) findBounds(layer GameContext) image.Rectangle {
minX, minY := math.MaxFloat64, math.MaxFloat64
maxX, maxY := -math.MaxFloat64, -math.MaxFloat64
for _, seg := range p.GetHitbox(layer) {
minX = math.Min(float64(seg.Min.X), minX)
minY = math.Min(float64(seg.Min.Y), minY)
maxX = math.Max(float64(seg.Max.X), maxX)
maxY = math.Max(float64(seg.Max.Y), maxY)
}
bounds := image.Rect(int(minX), int(minY), int(maxX), int(maxY))
return bounds
}

func (p Player) GetZ() int {
return 0
}
Expand All @@ -97,22 +113,123 @@ func (p *Player) Update(collidables []HasHitbox, climbables []Climbable, rivers

func (p *Player) handleInteractions(interactables []HasHitbox) {
if p.RiverJumping {
var objectToJump HasHitbox = nil

for _, c := range interactables {
if p.Overlaps(Interaction, c.GetHitbox(Interaction)) {
objectToJump = c
break
}
newPos, found := p.GetSmallestJump(interactables)

if found {
p.hitbox = p.hitbox.Sub(p.hitbox.Min).Add(newPos)
offset := p.Origin(Collision).Y - p.hitbox.Min.Y
p.hitbox = p.hitbox.Sub(image.Pt(0, offset))
}
if objectToJump == nil {
return
}
}

func (p Player) GetSmallestJump(jumpables []HasHitbox) (point image.Point, found bool) {
origin := p.Origin(Collision)
size := p.Size(Collision)

smallestJumpDist := math.MaxFloat64
smallestJump := image.Point{}
for _, jumpable := range jumpables {
if !jumpable.Overlaps(Interaction, p.GetHitbox(Collision)) {
continue
}

//for now jump to top corner will need to properly re-implement at some point
newPos := objectToJump.Origin(Collision).Sub(image.Point{p.hitbox.Dx(), p.hitbox.Dy()})
for id, seg := range jumpable.GetHitbox(Interaction) {
if !p.Overlaps(Collision, []image.Rectangle{seg}) {
continue
}
segHitbox := jumpable.GetHitbox(Collision)[id]

if origin.X >= segHitbox.Max.X { //right
newPoint := testJump(jumpable, segHitbox,
func(segment image.Rectangle) image.Point {
return image.Pt(segment.Min.X, origin.Y).Sub(image.Pt(size.X, 0))
},
func(origin image.Point) image.Rectangle {
return image.Rectangle{origin, origin.Add(size)}
},
)

updateJumpIfSmaller(origin, newPoint, &smallestJumpDist, &smallestJump)
} else if origin.X <= segHitbox.Min.X { //left
newPoint := testJump(jumpable, segHitbox,
func(segment image.Rectangle) image.Point {
return image.Pt(segment.Max.X, origin.Y)
},
func(origin image.Point) image.Rectangle {
return image.Rectangle{origin, origin.Add(size)}
},
)

updateJumpIfSmaller(origin, newPoint, &smallestJumpDist, &smallestJump)
}

if origin.Y >= segHitbox.Max.Y { //bottom
newPoint := testJump(jumpable, segHitbox,
func(segment image.Rectangle) image.Point {
return image.Pt(origin.X, segment.Min.Y).Sub(image.Pt(0, size.Y))
},
func(origin image.Point) image.Rectangle {
return image.Rectangle{origin, origin.Add(size)}
},
)

updateJumpIfSmaller(origin, newPoint, &smallestJumpDist, &smallestJump)
} else if origin.Y <= segHitbox.Min.Y { //top
newPoint := testJump(jumpable, segHitbox,
func(segment image.Rectangle) image.Point {
return image.Pt(origin.X, segment.Max.Y)
},
func(origin image.Point) image.Rectangle {
return image.Rectangle{origin, origin.Add(size)}
},
)

updateJumpIfSmaller(origin, newPoint, &smallestJumpDist, &smallestJump)
}
}
}
return smallestJump, smallestJumpDist != math.MaxFloat64
}

// Finds the location the player would jump to taking into account multiple
// segments that might need to be jumped. If the player would land in another
// segment it continues to test further jumps on that new segment.
//
// Once that new land location is found it gets returned.
//
// makeJump returns the origin of the new player hitbox after jumping the given
// segment. This function handles how the jump should be made (what direction)
//
// pRect generates a version of the player's hitbox to test for collisions after
// each jump without actually moving the player yet. It should just return a hitbox
// of the player's size with it's origin at the provided point.
func testJump(fullObj HasHitbox, jumpSeg image.Rectangle, makeJump func(image.Rectangle) image.Point, pRect func(image.Point) image.Rectangle) image.Point {
newPoint := makeJump(jumpSeg)

newRect := pRect(newPoint)
for fullObj.Overlaps(Collision, []image.Rectangle{newRect}) {
for _, newSegTest := range fullObj.GetHitbox(Collision) {
if !newRect.Overlaps(newSegTest) {
continue
}
newPoint = makeJump(newSegTest)
break
}
newRect = pRect(newPoint)
}
return newPoint
}

p.hitbox = p.hitbox.Sub(p.hitbox.Min).Add(newPos)
// Calculates jump distance between points before and new and updates the
// pointers with the new distance and point respectively if the new distance
// is smaller than the previous smallest.
func updateJumpIfSmaller(before image.Point, new image.Point, dist *float64, point *image.Point) {
delta := math.Hypot(float64(before.X-new.X), float64(before.Y-new.Y))
if delta < *dist {
*point = new
*dist = delta
}
}

Expand Down

0 comments on commit 649f080

Please sign in to comment.