Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 95 lines (82 sloc) 3.609 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
# Allow placed signs to behave as a teleporter when right clicked on. The format
# for the sign is line one containing 'Teleporter' and line two containing either:
# a) x, y, z (base 36 offset by +32000) b) {locs_plus_waypoint_name}. In the case of b it
# will substitute that name with the loc it represents when the sign is placed. You can write
# anything on lines 3 or lower and it will not affect how the sign works as a teleporter.
class PortsPlugin
  include Purugin::Plugin, Purugin::Colors
  description 'Ports', 0.3
  required :LocsPlus, :include => :CoordinateEncoding
  
  def teleporter_loc(state)
    return nil if state.lines.length < 2 || state.get_line(0) != "Teleporter"
    state.get_line(1)
  end
  
  SAFE_BLOCKS = [:air, :water]
  
  def safe_loc?(me, bottom_block)
    top_block = bottom_block.block_at(:up)
    floor_block = bottom_block.block_at(:down)
    top_block.is?(*SAFE_BLOCKS) && bottom_block.is?(*SAFE_BLOCKS) &&
      floor_block.solid?
  end

  # Attempts to find a safe location when block under your feet is solid
  # and where both blocks your player occupies are either air or water.
  # The algorithm is to try the location you want. If it does not work then
  # try all adjacent squares. If that does not work go _direction_ one and
  # repeat. This will fail after limit tries.
  def safe_loc(me, world, location, limit = 3, direction = -1)
    return nil if limit <= 0
    bottom_block = world.block_at(location)
    return location if safe_loc?(me, bottom_block)

    [:north, :east, :west, :south].each do |direction|
      alt_block = bottom_block.block_at(direction)
      return alt_block.location if safe_loc?(me, alt_block)
    end
      
    return safe_loc(me, world, org.bukkit.Location.new(world, location.x, location.y + direction, location.z), limit - 1, direction)
  end
  
  def on_enable
    event(:player_interact) do |e|
      if e.right_click_block? && e.clicked_block.is?(:sign_post, :wall_sign)
        loc = teleporter_loc e.clicked_block.state
        return unless loc
        
        if loc =~ /([^\x00-\x7F].)?([0-9a-z]+)\s*,\s*([0-9a-z]+)\s*,\s*([0-9a-z]+)/u
          x, y, z = $2, $3, $4
          loc = org.bukkit.Location.new e.player.world, decode(x), decode(y), decode(z)

          # Look from waypoint down
          destination = safe_loc(e.player, e.player.world, loc, 5, -1)
          # If no safe point then look up
          destination = safe_loc(e.player, e.player.world, loc, 5, 1) unless destination
          
          if destination
            server.scheduler.schedule_sync_delayed_task(self) { e.player.teleport destination }
          else
            e.player.msg red("Crud at destination!")
          end
        end
      end
    end

    # When signs are placed it will look for {my_waypoint} on the loc line. If it finds
    # this it will ask LocsPlus plugin if such a waypoint exists. If so it substitures the
    # the loc in for the name.
    event(:sign_change) do |e|
      loc = teleporter_loc e
      return unless loc
      if loc =~ /\{([^}]+)\}/
        waypoint = $1
        begin
          loc = LocsPlus().location(e.player, waypoint).to_a
        rescue ArgumentError
          e.player.msg red($!.message)
          break
        end
      end
      
      e.lines.to_a.each_with_index do |line, i|
        if i == 1 && loc
          e.set_line i, gray(loc[0..2].to_a.map {|l| encode(l) }.join(","))
        elsif i == 2 and line == "" && waypoint
          e.set_line i, green(waypoint)
        else
          e.set_line i, colorize(line)
        end
      end
    end
  end
end
Something went wrong with that request. Please try again.