diff --git a/player.go b/player.go index 8174ead..a8dfc29 100644 --- a/player.go +++ b/player.go @@ -1,11 +1,8 @@ package main import ( - v "bitbucket.org/hackerbots/vector" "code.google.com/p/go.net/websocket" "log" - "math" - "math/rand" ) type player struct { @@ -99,231 +96,3 @@ func (p *player) recv() { log.Printf("player %s: recv close", p.Robots[0].Id) p.ws.Close() } - -func (r *Robot) checkCollisions(g *game, move_vector v.Vector2d) (bool, v.Point2d, *Robot) { - collision := false - intersection_point := v.Point2d{X: 0, Y: 0} - - // Check Walls - r_walls := v.Rect2d{A: v.Point2d{X: 0, Y: 0}, B: v.Point2d{X: g.width, Y: g.height}} - collision, _, pos := v.RectIntersection(r_walls, r.Position, move_vector) - if collision { - return collision, pos, nil - } - - // Check Other Bots - for player := range g.players { - for _, bot := range player.Robots { - if bot.Id == r.Id { - continue - } - player_rect := v.RectFromPoint(bot.Position, 3) - collision, _, pos := v.RectIntersection(player_rect, r.Position, move_vector) - if collision { - return collision, pos, bot - } - } - } - - // Check Obstacles - for _, obj := range g.obstacles { - collision, _, pos := v.RectIntersection(obj.Bounds, r.Position, move_vector) - if collision { - return collision, pos, nil - } - } - - return collision, intersection_point, nil -} - -func (r *Robot) Tick(g *game) { - r.Collision = false - r.Hit = false - r.scan(g) - - // Adjust Speed - if r.Speed < r.TargetSpeed { - r.Speed += (r.Stats.Acceleration * delta) - if r.Speed > r.TargetSpeed { - r.Speed = r.TargetSpeed - } - - } else if float32(math.Abs(float64(r.Speed-r.TargetSpeed))) > v.Epsilon { - r.Speed -= (r.Stats.Acceleration * delta) - } else { - r.Speed = r.TargetSpeed - } - - // Adjust Heading - current_heading := r.Heading - if current_heading.Mag() == 0 && r.MoveTo != nil { - // We may have been stopped before this and had no heading - current_heading = r.MoveTo.Sub(r.Position).Normalize() - } - - new_heading := current_heading - if r.MoveTo != nil { - // Where do we WANT to be heading? - new_heading = r.MoveTo.Sub(r.Position).Normalize() - } - - if new_heading.Mag() > 0 { - // Is our direction change too much? Hard coding to 5 degrees/s for now - angle := v.Angle(current_heading, new_heading) * v.Rad2deg - - dir := 1.0 - if angle < 0 { - dir = -1.0 - } - - // Max turn radius in this case is in degrees per second - if float32(math.Abs(float64(angle))) > (float32(r.Stats.TurnSpeed) * delta) { - // New heading should be a little less, take current heading and - // rotate by the max turn radius per frame. - rot := (float32(r.Stats.TurnSpeed) * delta) * v.Deg2rad - - new_heading = current_heading.Rotate(rot * float32(dir)) - } - - move_vector := new_heading.Scale(r.Speed * delta) - collision, intersection_point, hit_robot := r.checkCollisions(g, move_vector) - if collision { - r.Collision = true - if hit_robot != nil { - hit_robot.Health -= int(r.Speed / 10.0) - hit_robot.Speed = (hit_robot.Speed * 0.1) - hit_robot.Heading = r.Heading - } - move_by := intersection_point.Sub(r.Position) - move_dist := move_by.Scale(float32(math.Floor(float64(move_by.Mag()-3.0))) / move_by.Mag()) - - r.Position = r.Position.Add(move_dist) - r.Health -= int(r.Speed / 10.0) - r.MoveTo = &r.Position - r.Speed = (r.Speed * 0.1) - r.Heading = r.Heading.Scale(-1.0) - } else { - r.Position = r.Position.Add(move_vector) - if new_heading.Mag() > 0 { - r.Heading = new_heading - } else { - log.Printf("Zero Heading %v", new_heading) - } - } - } - - // We only self repair when we're stopped - if math.Abs(float64(r.Speed)) < v.Epsilon && r.RepairCounter > 0 { - r.RepairCounter -= delta - if r.RepairCounter < 0 { - r.Health += g.repair_hp - r.RepairCounter = g.repair_rate - } - } - - // We only self repair when we're stopped - if math.Abs(float64(r.Speed)) < v.Epsilon && r.ActiveScan { - r.ScanCounter += delta * float32(r.Stats.ScannerRadius) * 0.1 - } else if r.ScanCounter > 0 { - r.ScanCounter -= delta * float32(r.Stats.ScannerRadius) * 0.05 - if r.ScanCounter <= 0 { - r.ScanCounter = 0 - } - } - - if r.FireAt != nil { - proj := r.fire(g.projectiles, g.turn) - if proj != nil { - g.projectiles[proj] = true - } - } - - if r.Probe != nil && r.ProbeResult == nil { - probe_vector := r.Probe.Sub(r.Position) - coll, pos, _ := r.checkCollisions(g, probe_vector) - if coll { - r.ProbeResult = &pos - } - } -} - -func (r *Robot) scan(g *game) { - r.Scanners = r.Scanners[:0] - for player, _ := range g.players { - for _, bot := range player.Robots { - if bot.Id == r.Id || bot.Health <= 0 { - continue - } - dist := v.Distance(bot.Position, r.Position) - if dist < float32(r.Stats.ScannerRadius+int(r.ScanCounter)) { - s := Scanner{ - Id: bot.Id, - Type: "robot", - } - r.Scanners = append(r.Scanners, s) - } - } - } - - for proj, _ := range g.projectiles { - if proj.Owner == r { - continue - } - - dist := v.Distance(proj.Position, r.Position) - if dist < float32(r.Stats.ScannerRadius+int(r.ScanCounter)) { - s := Scanner{ - Id: proj.Id, - Type: "projectile", - } - r.Scanners = append(r.Scanners, s) - } - } - -} - -func (r *Robot) fire(projectiles map[*Projectile]bool, turn int) *Projectile { - // Throttle the fire rate - time_since_fired := (float32(turn) * (delta * 1000)) - (float32(r.LastFired) * (delta * 1000)) - if time_since_fired < float32(r.Stats.FireRate) { - return nil - } - - r.LastFired = turn - - return &Projectile{ - Id: idg.Hash(), - Position: r.Position, - MoveTo: *r.FireAt, - Damage: r.Stats.WeaponDamage, - Radius: r.Stats.WeaponRadius, - Speed: r.Stats.WeaponSpeed, - Owner: r, - } -} - -func (r *Robot) reset(g *game) { - for { - start_pos := v.Point2d{ - X: rand.Float32() * float32(g.width), - Y: rand.Float32() * float32(g.height), - } - r.MoveTo = &start_pos - r.Position = start_pos - r.Health = r.Stats.Hp - - log.Printf("Reset %v", r) - - // Check Obstacles - retry := false - for _, obj := range g.obstacles { - _, inside, _ := v.RectIntersection(obj.Bounds, r.Position, v.Vector2d{0, 0}) - if inside { - retry = true - } - } - if !retry { - break - } - } -} diff --git a/robot.go b/robot.go index 9c5c54c..aaa8860 100644 --- a/robot.go +++ b/robot.go @@ -2,6 +2,9 @@ package main import ( v "bitbucket.org/hackerbots/vector" + "log" + "math" + "math/rand" ) type Robot struct { @@ -158,3 +161,231 @@ type Instruction struct { Repair *bool `json:"repair,omitempty"` Scan *bool `json:"scan,omitempty"` } + +func (r *Robot) checkCollisions(g *game, move_vector v.Vector2d) (bool, v.Point2d, *Robot) { + collision := false + intersection_point := v.Point2d{X: 0, Y: 0} + + // Check Walls + r_walls := v.Rect2d{A: v.Point2d{X: 0, Y: 0}, B: v.Point2d{X: g.width, Y: g.height}} + collision, _, pos := v.RectIntersection(r_walls, r.Position, move_vector) + if collision { + return collision, pos, nil + } + + // Check Other Bots + for player := range g.players { + for _, bot := range player.Robots { + if bot.Id == r.Id { + continue + } + player_rect := v.RectFromPoint(bot.Position, 3) + collision, _, pos := v.RectIntersection(player_rect, r.Position, move_vector) + if collision { + return collision, pos, bot + } + } + } + + // Check Obstacles + for _, obj := range g.obstacles { + collision, _, pos := v.RectIntersection(obj.Bounds, r.Position, move_vector) + if collision { + return collision, pos, nil + } + } + + return collision, intersection_point, nil +} + +func (r *Robot) Tick(g *game) { + r.Collision = false + r.Hit = false + r.scan(g) + + // Adjust Speed + if r.Speed < r.TargetSpeed { + r.Speed += (r.Stats.Acceleration * delta) + if r.Speed > r.TargetSpeed { + r.Speed = r.TargetSpeed + } + + } else if float32(math.Abs(float64(r.Speed-r.TargetSpeed))) > v.Epsilon { + r.Speed -= (r.Stats.Acceleration * delta) + } else { + r.Speed = r.TargetSpeed + } + + // Adjust Heading + current_heading := r.Heading + if current_heading.Mag() == 0 && r.MoveTo != nil { + // We may have been stopped before this and had no heading + current_heading = r.MoveTo.Sub(r.Position).Normalize() + } + + new_heading := current_heading + if r.MoveTo != nil { + // Where do we WANT to be heading? + new_heading = r.MoveTo.Sub(r.Position).Normalize() + } + + if new_heading.Mag() > 0 { + // Is our direction change too much? Hard coding to 5 degrees/s for now + angle := v.Angle(current_heading, new_heading) * v.Rad2deg + + dir := 1.0 + if angle < 0 { + dir = -1.0 + } + + // Max turn radius in this case is in degrees per second + if float32(math.Abs(float64(angle))) > (float32(r.Stats.TurnSpeed) * delta) { + // New heading should be a little less, take current heading and + // rotate by the max turn radius per frame. + rot := (float32(r.Stats.TurnSpeed) * delta) * v.Deg2rad + + new_heading = current_heading.Rotate(rot * float32(dir)) + } + + move_vector := new_heading.Scale(r.Speed * delta) + collision, intersection_point, hit_robot := r.checkCollisions(g, move_vector) + if collision { + r.Collision = true + if hit_robot != nil { + hit_robot.Health -= int(r.Speed / 10.0) + hit_robot.Speed = (hit_robot.Speed * 0.1) + hit_robot.Heading = r.Heading + } + move_by := intersection_point.Sub(r.Position) + move_dist := move_by.Scale(float32(math.Floor(float64(move_by.Mag()-3.0))) / move_by.Mag()) + + r.Position = r.Position.Add(move_dist) + r.Health -= int(r.Speed / 10.0) + r.MoveTo = &r.Position + r.Speed = (r.Speed * 0.1) + r.Heading = r.Heading.Scale(-1.0) + } else { + r.Position = r.Position.Add(move_vector) + if new_heading.Mag() > 0 { + r.Heading = new_heading + } else { + log.Printf("Zero Heading %v", new_heading) + } + } + } + + // We only self repair when we're stopped + if math.Abs(float64(r.Speed)) < v.Epsilon && r.RepairCounter > 0 { + r.RepairCounter -= delta + if r.RepairCounter < 0 { + r.Health += g.repair_hp + r.RepairCounter = g.repair_rate + } + } + + // We only self repair when we're stopped + if math.Abs(float64(r.Speed)) < v.Epsilon && r.ActiveScan { + r.ScanCounter += delta * float32(r.Stats.ScannerRadius) * 0.1 + } else if r.ScanCounter > 0 { + r.ScanCounter -= delta * float32(r.Stats.ScannerRadius) * 0.05 + if r.ScanCounter <= 0 { + r.ScanCounter = 0 + } + } + + if r.FireAt != nil { + proj := r.fire(g.projectiles, g.turn) + if proj != nil { + g.projectiles[proj] = true + } + } + + if r.Probe != nil && r.ProbeResult == nil { + probe_vector := r.Probe.Sub(r.Position) + coll, pos, _ := r.checkCollisions(g, probe_vector) + if coll { + r.ProbeResult = &pos + } + } +} + +func (r *Robot) scan(g *game) { + r.Scanners = r.Scanners[:0] + for player, _ := range g.players { + for _, bot := range player.Robots { + if bot.Id == r.Id || bot.Health <= 0 { + continue + } + dist := v.Distance(bot.Position, r.Position) + if dist < float32(r.Stats.ScannerRadius+int(r.ScanCounter)) { + s := Scanner{ + Id: bot.Id, + Type: "robot", + } + r.Scanners = append(r.Scanners, s) + } + } + } + + for proj, _ := range g.projectiles { + if proj.Owner == r { + continue + } + + dist := v.Distance(proj.Position, r.Position) + if dist < float32(r.Stats.ScannerRadius+int(r.ScanCounter)) { + s := Scanner{ + Id: proj.Id, + Type: "projectile", + } + r.Scanners = append(r.Scanners, s) + } + } + +} + +func (r *Robot) fire(projectiles map[*Projectile]bool, turn int) *Projectile { + // Throttle the fire rate + time_since_fired := (float32(turn) * (delta * 1000)) - (float32(r.LastFired) * (delta * 1000)) + if time_since_fired < float32(r.Stats.FireRate) { + return nil + } + + r.LastFired = turn + + return &Projectile{ + Id: idg.Hash(), + Position: r.Position, + MoveTo: *r.FireAt, + Damage: r.Stats.WeaponDamage, + Radius: r.Stats.WeaponRadius, + Speed: r.Stats.WeaponSpeed, + Owner: r, + } +} + +func (r *Robot) reset(g *game) { + for { + start_pos := v.Point2d{ + X: rand.Float32() * float32(g.width), + Y: rand.Float32() * float32(g.height), + } + r.MoveTo = &start_pos + r.Position = start_pos + r.Health = r.Stats.Hp + + log.Printf("Reset %v", r) + + // Check Obstacles + retry := false + for _, obj := range g.obstacles { + _, inside, _ := v.RectIntersection(obj.Bounds, r.Position, v.Vector2d{0, 0}) + if inside { + retry = true + } + } + if !retry { + break + } + } +}