func distFromPointToSeg(p linear.Vec2, s linear.Seg2) float64 { s.P = s.P.Sub(p) s.Q = s.Q.Sub(p) cross := s.Ray().Cross() crossSeg := linear.Seg2{Q: cross} if crossSeg.Left(s.P) != crossSeg.Left(s.Q) { return s.DistFromOrigin() } da := s.P.Mag() db := s.Q.Mag() if da < db { return da } return db }
func (e addRiftWalkFireEvent) Apply(_g interface{}) { g := _g.(*game.Game) player, ok := g.Ents[e.PlayerGid].(*game.PlayerEnt) if !ok { return } proc := player.Processes[100+e.ProcessId] if proc == nil { return } rwProc, ok := proc.(*riftWalkProcess) if !ok { return } if rwProc.Stored.Magnitude() <= rwProc.Threshold { return } dist, radius := rwProc.GetVals() rwProc.Stored = game.Mana{} movement := (linear.Vec2{dist, 0}).Rotate(player.Angle) dest := player.Pos().Add(movement) inside := false // Check if the player would end up inside a polygon for _, poly := range g.Levels[player.Level()].Room.Walls { if linear.VecInsideConvexPoly(dest, poly) { inside = true break } } // If the player would end up inside a polygon then intersect the segment the // player would move along with all line segements and move the player to the // nearest intersection point. if inside { moveSeg := linear.Seg2{player.Pos(), dest} closest := dest var collisionSeg linear.Seg2 for _, poly := range g.Levels[player.Level()].Room.Walls { for i := range poly { seg := poly.Seg(i) if seg.DoesIsect(moveSeg) { isect := seg.Isect(moveSeg) if isect.Sub(player.Pos()).Mag2() < closest.Sub(player.Pos()).Mag2() { closest = isect collisionSeg = seg } } } } if collisionSeg.Ray().Mag2() == 0 { dest = closest.Sub(movement.Norm().Scale(player.Stats().Size() * 2)) } else { nudge := collisionSeg.Ray().Norm().Cross().Scale(player.Stats().Size()) dest = closest.Add(nudge) } } for _, ent := range g.Ents { if ent == player { continue } doDamage := false if ent.Pos().Sub(dest).Mag() <= radius+ent.Stats().Size() { angle := dest.Sub(ent.Pos()).Angle() ent.ApplyForce((linear.Vec2{-rwProc.Force, 0}).Rotate(angle)) doDamage = true } else { ray := dest.Sub(player.Pos()) perp := ray.Cross().Norm() scaledPerp := perp.Scale(ent.Stats().Size() + player.Stats().Size()) sideRay0 := linear.Seg2{player.Pos().Add(scaledPerp), dest.Add(scaledPerp)} sideRay1 := linear.Seg2{player.Pos().Sub(scaledPerp), dest.Sub(scaledPerp)} if sideRay0.Left(ent.Pos()) != sideRay1.Left(ent.Pos()) { // We know the ent lies between sideRays 0 and 1, now we need to make // sure it lies between src and dst. forward := ent.Pos().Sub(dest) backward := ent.Pos().Sub(player.Pos()) if (forward.Dot(ray) < 0) != (backward.Dot(ray) < 0) { if (linear.Seg2{player.Pos(), dest}).Left(ent.Pos()) { ent.ApplyForce(perp.Scale(rwProc.Force)) } else { ent.ApplyForce(perp.Scale(-rwProc.Force)) } doDamage = true } } } if doDamage { ent.Stats().ApplyDamage(stats.Damage{stats.DamageFire, 50}) } } player.SetPos(dest) }