Class: Player

Inherits:
Entity show all
Includes:
Comparable
Defined in:
lib/game_2d/player.rb

Overview

The base Player class representing what all Players have in common Moves can be enqueued by calling add_move Calling update() causes a move to be dequeued and executed, applying forces to the game object

The server instantiates this class to represent each connected player

Constant Summary collapse

BUILD_TIME =

Game ticks it takes before a block’s HP is raised by 1

7
BRAKE_SPEED =

Amount to decelerate each tick when braking

4

Constants included from EntityConstants

EntityConstants::CELL_WIDTH_IN_PIXELS, EntityConstants::MAX_VELOCITY, EntityConstants::PIXEL_WIDTH, EntityConstants::WIDTH

Instance Attribute Summary collapse

Attributes inherited from Entity

#a, #moving, #space, #x, #x_vel, #y, #y_vel

Instance Method Summary collapse

Methods inherited from Entity

#accelerate, #angle_to_vector, #bottom_cell_y, #direction_to, #doomed?, #draw_angle, #drop_diagonal, #empty_above?, #empty_on_left?, #empty_on_right?, #empty_underneath?, #entities_obstructing, #going_past_entity, #grab!, #grabbed?, #harmed_by, #i_hit, #left_cell_x, #move, #move_x, #move_y, #moving?, #next_to, #occupied_cells, #opaque, #pixel_x, #pixel_y, #release!, #right_cell_x, #should_fall?, #top_cell_y, #vector_to_angle, #wake!, #warp

Methods included from Entity::ClassMethods

#bottom_cell_y_at, #constrain_velocity, #left_cell_x_at, #right_cell_x_at, #top_cell_y_at

Methods included from Transparency

#transparent?

Methods included from Registerable

#nullsafe_registry_id, #registry_id, #registry_id=, #registry_id?, #registry_id_safe

Methods included from Serializable

#<=>, #==, as_json, #eql?, from_json, #hash, #to_json

Constructor Details

#initialize(player_name = "<unknown>") ⇒ Player

Returns a new instance of Player.



27
28
29
30
31
32
33
34
35
36
# File 'lib/game_2d/player.rb', line 27

def initialize(player_name = "<unknown>")
  super
  @player_name = player_name
  @score = 0
  @moves = []
  @current_move = nil
  @build_block_id = nil
  @build_level = 0
  @complex_move = nil
end

Instance Attribute Details

#build_block_idObject

Returns the value of attribute build_block_id.



25
26
27
# File 'lib/game_2d/player.rb', line 25

def build_block_id
  @build_block_id
end

#player_nameObject

Returns the value of attribute player_name.



24
25
26
# File 'lib/game_2d/player.rb', line 24

def player_name
  @player_name
end

#scoreObject

Returns the value of attribute score.



24
25
26
# File 'lib/game_2d/player.rb', line 24

def score
  @score
end

Instance Method Details

#add_move(new_move) ⇒ Object

Accepts a hash, with a key :move => move_type



206
207
208
209
# File 'lib/game_2d/player.rb', line 206

def add_move(new_move)
  return unless new_move
  @moves << new_move
end

#all_stateObject



215
216
217
218
# File 'lib/game_2d/player.rb', line 215

def all_state
  super.unshift(player_name).push(
    score, build_block_id, @complex_move)
end

#as_jsonObject



220
221
222
223
224
225
226
227
# File 'lib/game_2d/player.rb', line 220

def as_json
  super.merge!(
    :player_name => player_name,
    :score => score,
    :build_block => @build_block_id,
    :complex_move => @complex_move.as_json
  )
end

#brakeObject



148
149
150
151
152
153
154
# File 'lib/game_2d/player.rb', line 148

def brake
  if @x_vel.zero?
    self.y_vel = brake_velocity(@y_vel)
  else
    self.x_vel = brake_velocity(@x_vel)
  end
end

#brake_velocity(v) ⇒ Object



156
157
158
159
160
# File 'lib/game_2d/player.rb', line 156

def brake_velocity(v)
  return 0 if v.abs < BRAKE_SPEED
  sign = v <=> 0
  sign * (v.abs - BRAKE_SPEED)
end

#buildObject

Create the actual block



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/game_2d/player.rb', line 174

def build
  if building?
    @build_level += 1
    if @build_level >= BUILD_TIME
      @build_level = 0
      build_block.hp += 1
    end
  else
    bb = Entity::Block.new(@x, @y)
    bb.owner_id = registry_id
    bb.hp = 1
    @space << bb # generates an ID
    @build_block_id = bb.registry_id
    @build_level = 0
  end
end

#build_blockObject



53
54
55
56
57
# File 'lib/game_2d/player.rb', line 53

def build_block
  return nil unless building?
  fail "Can't look up build_block when not in a space" unless @space
  @space[@build_block_id] or fail "Don't have build_block #{@build_block_id}"
end

#building?Boolean

Returns:

  • (Boolean)


51
# File 'lib/game_2d/player.rb', line 51

def building?; @build_block_id; end

#check_for_disown_blockObject



193
194
195
196
197
198
199
# File 'lib/game_2d/player.rb', line 193

def check_for_disown_block
  return unless building?
  return if @space.entities_overlapping(@x, @y).include?(build_block)
  build_block.owner_id = nil
  build_block.wake!
  disown_block
end

#destroy!Object



59
60
61
# File 'lib/game_2d/player.rb', line 59

def destroy!
  build_block.owner_id = nil if building?
end

#disown_blockObject



191
# File 'lib/game_2d/player.rb', line 191

def disown_block; $stderr.puts "#{self} disowning #{build_block}"; @build_block_id, @build_level = nil, 0; end

#draw(window) ⇒ Object



241
242
243
244
245
246
247
# File 'lib/game_2d/player.rb', line 241

def draw(window)
  super
  window.font.draw_rel(player_name,
    pixel_x + CELL_WIDTH_IN_PIXELS / 2, pixel_y, ZOrder::Text,
    0.5, 1.0, # Centered X; above Y
    1.0, 1.0, Gosu::Color::YELLOW)
end

#draw_zorderObject



239
# File 'lib/game_2d/player.rb', line 239

def draw_zorder; ZOrder::Player end

#falling?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/game_2d/player.rb', line 43

def falling?
  underfoot.empty?
end

#fire(x_vel, y_vel) ⇒ Object

Create the actual pellet



167
168
169
170
171
# File 'lib/game_2d/player.rb', line 167

def fire(x_vel, y_vel)
  pellet = Entity::Pellet.new(@x, @y, 0, x_vel, y_vel)
  pellet.owner = self
  @space << pellet
end

#flipObject



162
163
164
# File 'lib/game_2d/player.rb', line 162

def flip
  self.a += 180
end

#image_filenameObject



237
# File 'lib/game_2d/player.rb', line 237

def image_filename; "player.png"; end

#rise_upObject



201
202
203
# File 'lib/game_2d/player.rb', line 201

def rise_up
  @complex_move = Move::RiseUp.new(self)
end

#sleep_now?Boolean

Returns:

  • (Boolean)


38
# File 'lib/game_2d/player.rb', line 38

def sleep_now?; false; end

#slide(dir) ⇒ Object



140
141
142
143
144
145
146
# File 'lib/game_2d/player.rb', line 140

def slide(dir)
  if opaque(next_to(dir)).empty?
    accelerate(*angle_to_vector(dir))
  else
    self.a = dir + 180
  end
end

#slide_leftObject



137
# File 'lib/game_2d/player.rb', line 137

def slide_left; slide(self.a - 90); end

#slide_rightObject



138
# File 'lib/game_2d/player.rb', line 138

def slide_right; slide(self.a + 90); end

#to_sObject



211
212
213
# File 'lib/game_2d/player.rb', line 211

def to_s
  "#{player_name} (#{registry_id_safe}) at #{x}x#{y}"
end

#underfootObject



40
41
42
# File 'lib/game_2d/player.rb', line 40

def underfoot
  opaque(next_to(self.a + 180))
end

#updateObject



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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/game_2d/player.rb', line 63

def update
  fail "No space set for #{self}" unless @space
  check_for_disown_block

  if @complex_move
    # returns true if more work to do
    return if @complex_move.update(self)
    @complex_move.on_completion(self)
    @complex_move = nil
  end

  if falling = falling?
    self.a = 0
    accelerate(0, 1)
  end

  args = @moves.shift
  case (current_move = args.delete(:move).to_sym)
    when :slide_left, :slide_right, :brake, :flip, :build, :rise_up
      send current_move unless falling
    when :fire
      fire args[:x_vel], args[:y_vel]
    else
      puts "Invalid move for #{self}: #{current_move}, #{args.inspect}"
  end if args

  # Only go around corner if sitting on exactly one object
  blocks_underfoot = underfoot
  if blocks_underfoot.size == 1
    other = blocks_underfoot.first
    # Figure out where corner is and whether we're about to reach or pass it
    corner, distance, overshoot, turn = going_past_entity(other.x, other.y)
    if corner
      original_speed = @x_vel.abs + @y_vel.abs
      original_dir = vector_to_angle
      new_dir = original_dir + turn

      # Make sure nothing occupies any space we're about to move through
      if opaque(
        @space.entities_overlapping(*corner) + next_to(new_dir, *corner)
      ).empty?
        # Move to the corner
        self.x_vel, self.y_vel = angle_to_vector(original_dir, distance)
        move

        # Turn and apply remaining velocity
        # Make sure we move at least one subpixel so we don't sit exactly at
        # the corner, and fall
        self.a += turn
        overshoot = 1 if overshoot.zero?
        self.x_vel, self.y_vel = angle_to_vector(new_dir, overshoot)
        move

        self.x_vel, self.y_vel = angle_to_vector(new_dir, original_speed)
      else
        # Something's in the way -- possibly in front of us, or possibly
        # around the corner
        move
      end
    else
      # Not yet reaching the corner -- or making a diagonal motion, for which
      # we can't support going around the corner
      move
    end
  else
    # Straddling two objects, or falling
    move
  end

  # Check again whether we've moved off of a block
  # we were building
  check_for_disown_block
end

#update_from_json(json) ⇒ Object



229
230
231
232
233
234
235
# File 'lib/game_2d/player.rb', line 229

def update_from_json(json)
  @player_name = json[:player_name] if json[:player_name]
  @score = json[:score] if json[:score]
  @build_block_id = json[:build_block].try(:to_sym) if json[:build_block]
  @complex_move = Serializable.from_json(json[:complex_move]) if json[:complex_move]
  super
end