class YouSoBasicGorillas
attr_accessor :outputs, :grid, :state, :inputs
def tick
defaults
render
calc
process_inputs
end
def defaults
outputs.background_color = [33, 32, 87]
state.building_spacing = 1
state.building_room_spacing = 15
state.building_room_width = 10
state.building_room_height = 15
state.building_heights = [4, 4, 6, 8, 15, 20, 18]
state.building_room_sizes = [5, 4, 6, 7]
state.gravity = 0.25
state.first_strike ||= :player_1
state.buildings ||= []
state.holes ||= []
state.player_1_score ||= 0
state.player_2_score ||= 0
state.wind ||= 0
end
def render
render_stage
render_value_insertion
render_gorillas
render_holes
render_banana
render_game_over
render_score
render_wind
end
def render_score
outputs.primitives << [0, 0, 1280, 31, fancy_white].solid
outputs.primitives << [1, 1, 1279, 29].solid
outputs.labels << [ 10, 25, "Score: #{state.player_1_score}", 0, 0, fancy_white]
outputs.labels << [1270, 25, "Score: #{state.player_2_score}", 0, 2, fancy_white]
end
def render_wind
outputs.primitives << [640, 12, state.wind * 500 + state.wind * 10 * rand, 4, 35, 136, 162].solid
outputs.lines << [640, 30, 640, 0, fancy_white]
end
def render_game_over
return unless state.over
outputs.primitives << [grid.rect, 0, 0, 0, 200].solid
outputs.primitives << [640, 370, "Game Over!!", 5, 1, fancy_white].label
if state.winner == :player_1
outputs.primitives << [640, 340, "Player 1 Wins!!", 5, 1, fancy_white].label
else
outputs.primitives << [640, 340, "Player 2 Wins!!", 5, 1, fancy_white].label
end
end
def render_stage
return unless state.stage_generated
return if state.stage_rendered
outputs.static_solids << [grid.rect, 33, 32, 87]
outputs.static_solids << state.buildings.map(&:solids)
state.stage_rendered = true
end
def render_gorilla gorilla, id
return unless gorilla
if state.banana && state.banana.owner == gorilla
animation_index = state.banana.created_at.frame_index(3, 5, false)
end
if !animation_index
outputs.sprites << [gorilla.solid, "sprites/#{id}-idle.png"]
else
outputs.sprites << [gorilla.solid, "sprites/#{id}-#{animation_index}.png"]
end
end
def render_gorillas
render_gorilla state.player_1, :left
render_gorilla state.player_2, :right
end
def render_value_insertion
return if state.banana
return if state.over
if state.current_turn == :player_1_angle
outputs.labels << [ 10, 710, "Angle: #{state.player_1_angle}_", fancy_white]
elsif state.current_turn == :player_1_velocity
outputs.labels << [ 10, 710, "Angle: #{state.player_1_angle}", fancy_white]
outputs.labels << [ 10, 690, "Velocity: #{state.player_1_velocity}_", fancy_white]
elsif state.current_turn == :player_2_angle
outputs.labels << [1120, 710, "Angle: #{state.player_2_angle}_", fancy_white]
elsif state.current_turn == :player_2_velocity
outputs.labels << [1120, 710, "Angle: #{state.player_2_angle}", fancy_white]
outputs.labels << [1120, 690, "Velocity: #{state.player_2_velocity}_", fancy_white]
end
end
def render_banana
return unless state.banana
rotation = state.tick_count.%(360) * 20
rotation *= -1 if state.banana.dx > 0
outputs.sprites << [state.banana.x, state.banana.y, 15, 15, 'sprites/banana.png', rotation]
end
def render_holes
outputs.sprites << state.holes.map do |s|
animation_index = s.created_at.frame_index(7, 3, false)
if animation_index
[s.sprite, [s.sprite.rect, "sprites/explosion#{animation_index}.png" ]]
else
s.sprite
end
end
end
def calc
calc_generate_stage
calc_current_turn
calc_banana
end
def calc_current_turn
return if state.current_turn
state.current_turn = :player_1_angle
state.current_turn = :player_2_angle if state.first_strike == :player_2
end
def calc_generate_stage
return if state.stage_generated
state.buildings << building_prefab(state.building_spacing + -20, *random_building_size)
8.numbers.inject(state.buildings) do |buildings, i|
buildings <<
building_prefab(state.building_spacing +
state.buildings.last.right,
*random_building_size)
end
building_two = state.buildings[1]
state.player_1 = new_player(building_two.x + building_two.w.fdiv(2),
building_two.h)
building_nine = state.buildings[-3]
state.player_2 = new_player(building_nine.x + building_nine.w.fdiv(2),
building_nine.h)
state.stage_generated = true
state.wind = 1.randomize(:ratio, :sign)
end
def new_player x, y
state.new_entity(:gorilla) do |p|
p.x = x - 25
p.y = y
p.solid = [p.x, p.y, 50, 50]
end
end
def calc_banana
return unless state.banana
state.banana.x += state.banana.dx
state.banana.dx += state.wind.fdiv(50)
state.banana.y += state.banana.dy
state.banana.dy -= state.gravity
banana_collision = [state.banana.x, state.banana.y, 10, 10]
if state.player_1 && banana_collision.intersect_rect?(state.player_1.solid)
state.over = true
if state.banana.owner == state.player_2
state.winner = :player_2
else
state.winner = :player_1
end
state.player_2_score += 1
elsif state.player_2 && banana_collision.intersect_rect?(state.player_2.solid)
state.over = true
if state.banana.owner == state.player_2
state.winner = :player_1
else
state.winner = :player_2
end
state.player_1_score += 1
end
if state.over
place_hole
return
end
return if state.holes.any? do |h|
h.sprite.scale_rect(0.8, 0.5, 0.5).intersect_rect? [state.banana.x, state.banana.y, 10, 10]
end
return unless state.banana.y < 0 || state.buildings.any? do |b|
b.rect.intersect_rect? [state.banana.x, state.banana.y, 1, 1]
end
place_hole
end
def place_hole
return unless state.banana
state.holes << state.new_entity(:banana) do |b|
b.sprite = [state.banana.x - 20, state.banana.y - 20, 40, 40, 'sprites/hole.png']
end
state.banana = nil
end
def process_inputs_main
return if state.banana
return if state.over
if inputs.keyboard.key_down.enter
input_execute_turn
elsif inputs.keyboard.key_down.backspace
state.as_hash[state.current_turn] ||= ""
state.as_hash[state.current_turn] = state.as_hash[state.current_turn][0..-2]
elsif inputs.keyboard.key_down.char
state.as_hash[state.current_turn] ||= ""
state.as_hash[state.current_turn] += inputs.keyboard.key_down.char
end
end
def process_inputs_game_over
return unless state.over
return unless inputs.keyboard.key_down.truthy_keys.any?
state.over = false
outputs.static_solids.clear
state.buildings.clear
state.holes.clear
state.stage_generated = false
state.stage_rendered = false
if state.first_strike == :player_1
state.first_strike = :player_2
else
state.first_strike = :player_1
end
end
def process_inputs
process_inputs_main
process_inputs_game_over
end
def input_execute_turn
return if state.banana
if state.current_turn == :player_1_angle && parse_or_clear!(:player_1_angle)
state.current_turn = :player_1_velocity
elsif state.current_turn == :player_1_velocity && parse_or_clear!(:player_1_velocity)
state.current_turn = :player_2_angle
state.banana =
new_banana(state.player_1,
state.player_1.x + 25,
state.player_1.y + 60,
state.player_1_angle,
state.player_1_velocity)
elsif state.current_turn == :player_2_angle && parse_or_clear!(:player_2_angle)
state.current_turn = :player_2_velocity
elsif state.current_turn == :player_2_velocity && parse_or_clear!(:player_2_velocity)
state.current_turn = :player_1_angle
state.banana =
new_banana(state.player_2,
state.player_2.x + 25,
state.player_2.y + 60,
180 - state.player_2_angle,
state.player_2_velocity)
end
if state.banana
state.player_1_angle = nil
state.player_1_velocity = nil
state.player_2_angle = nil
state.player_2_velocity = nil
end
end
def random_building_size
[state.building_heights.sample, state.building_room_sizes.sample]
end
def int? v
v.to_i.to_s == v.to_s
end
def random_building_color
[[ 99, 0, 107],
[ 35, 64, 124],
[ 35, 136, 162],
].sample
end
def random_window_color
[[ 88, 62, 104],
[253, 224, 187]].sample
end
def windows_for_building starting_x, floors, rooms
floors.-(1).combinations(rooms - 1).map do |floor, room|
[starting_x +
state.building_room_width.*(room) +
state.building_room_spacing.*(room + 1),
state.building_room_height.*(floor) +
state.building_room_spacing.*(floor + 1),
state.building_room_width,
state.building_room_height,
random_window_color]
end
end
def building_prefab starting_x, floors, rooms
state.new_entity(:building) do |b|
b.x = starting_x
b.y = 0
b.w = state.building_room_width.*(rooms) +
state.building_room_spacing.*(rooms + 1)
b.h = state.building_room_height.*(floors) +
state.building_room_spacing.*(floors + 1)
b.right = b.x + b.w
b.rect = [b.x, b.y, b.w, b.h]
b.solids = [[b.x - 1, b.y, b.w + 2, b.h + 1, fancy_white],
[b.x, b.y, b.w, b.h, random_building_color],
windows_for_building(b.x, floors, rooms)]
end
end
def parse_or_clear! game_prop
if int? state.as_hash[game_prop]
state.as_hash[game_prop] = state.as_hash[game_prop].to_i
return true
end
state.as_hash[game_prop] = nil
return false
end
def new_banana owner, x, y, angle, velocity
state.new_entity(:banana) do |b|
b.owner = owner
b.x = x
b.y = y
b.angle = angle % 360
b.velocity = velocity / 5
b.dx = b.angle.vector_x(b.velocity)
b.dy = b.angle.vector_y(b.velocity)
end
end
def fancy_white
[253, 252, 253]
end
end
$you_so_basic_gorillas = YouSoBasicGorillas.new
def tick args
$you_so_basic_gorillas.outputs = args.outputs
$you_so_basic_gorillas.grid = args.grid
$you_so_basic_gorillas.state = args.state
$you_so_basic_gorillas.inputs = args.inputs
$you_so_basic_gorillas.tick
end