Initial Commit

This commit is contained in:
2018-06-07 10:03:07 -05:00
commit b4852fbbd6
22 changed files with 1255 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/

5
.travis.yml Normal file
View File

@@ -0,0 +1,5 @@
sudo: false
language: ruby
rvm:
- 2.5.0
before_install: gem install bundler -v 1.16.1

6
Gemfile Normal file
View File

@@ -0,0 +1,6 @@
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
# Specify your gem's dependencies in cyberarm_engine.gemspec
gemspec

21
LICENSE.txt Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Cyberarm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

43
README.md Normal file
View File

@@ -0,0 +1,43 @@
# CyberarmEngine
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/cyberarm_engine`. To experiment with that code, run `bin/console` for an interactive prompt.
TODO: Delete this and the text above, and describe your gem
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'cyberarm_engine'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install cyberarm_engine
## Usage
TODO: Write usage instructions here
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/cyberarm_engine. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
## Code of Conduct
Everyone interacting in the CyberarmEngine projects codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/cyberarm_engine/blob/master/CODE_OF_CONDUCT.md).

10
Rakefile Normal file
View File

@@ -0,0 +1,10 @@
require "bundler/gem_tasks"
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList["test/**/*_test.rb"]
end
task :default => :test

14
bin/console Normal file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env ruby
require "bundler/setup"
require "cyberarm_engine"
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start
require "irb"
IRB.start(__FILE__)

8
bin/setup Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx
bundle install
# Do any other automated setup that you need to do here

38
cyberarm_engine.gemspec Normal file
View File

@@ -0,0 +1,38 @@
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "cyberarm_engine/version"
Gem::Specification.new do |spec|
spec.name = "cyberarm_engine"
spec.version = CyberarmEngine::VERSION
spec.authors = ["Cyberarm"]
spec.email = ["matthewlikesrobots@gmail.com"]
spec.summary = %q{TODO: Write a short summary, because RubyGems requires one.}
spec.description = %q{TODO: Write a longer description or delete this line.}
spec.homepage = "TODO: Put your gem's website or public repo URL here."
spec.license = "MIT"
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
# to allow pushing to a single host or delete this section to allow pushing to any host.
if spec.respond_to?(:metadata)
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
else
raise "RubyGems 2.0 or newer is required to protect against " \
"public gem pushes."
end
spec.files = `git ls-files -z`.split("\x0").reject do |f|
f.match(%r{^(test|spec|features)/})
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.add_dependency "gosu", "~> 0.13.3"
spec.add_development_dependency "bundler", "~> 1.16"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "minitest", "~> 5.0"
end

15
lib/cyberarm_engine.rb Normal file
View File

@@ -0,0 +1,15 @@
require "gosu"
require_relative "cyberarm_engine/version"
require_relative "cyberarm_engine/game_object"
require_relative "cyberarm_engine/game_state"
require_relative "cyberarm_engine/engine"
require_relative "cyberarm_engine/objects/text"
require_relative "cyberarm_engine/objects/multi_line_text"
require_relative "cyberarm_engine/ui/button"
require_relative "cyberarm_engine/ui/check_box"
require_relative "cyberarm_engine/ui/input"
require_relative "cyberarm_engine/ui/container"

View File

@@ -0,0 +1,80 @@
module CyberarmEngine
class Engine < Gosu::Window
attr_accessor :show_cursor
attr_reader :current_game_state, :last_game_state, :last_frame_time
def self.now
Gosu.milliseconds
end
def self.dt
$window.last_frame_time/1000.0
end
def initialize(width = 800, height = 600, fullscreen = false, update_interval = 1000.0/60)
@show_cursor = false
super(width, height, fullscreen, update_interval)
$window = self
@last_frame_time = Gosu.milliseconds-1
@current_frame_time = Gosu.milliseconds
self.caption = "CyberarmEngine #{CyberarmEngine::VERSION} #{Gosu.language}"
setup if defined?(setup)
end
def draw
if @current_game_state.is_a?(GameState)
@current_game_state.draw
end
end
def update
if @current_game_state.is_a?(GameState)
@current_game_state.update
end
@last_frame_time = Gosu.milliseconds-@current_frame_time
@current_frame_time = Gosu.milliseconds
end
def needs_cursor?
@show_cursor
end
def dt
@last_frame_time/1000.0
end
def button_up(id)
@current_game_state.button_up(id) if @current_game_state
end
def push_game_state(klass, options={})
@last_game_state = @current_game_state if @current_game_state
if klass.instance_of?(klass.class) && defined?(klass.options)
@current_game_state = klass
else
klass.new(options)
end
end
def set_game_state(klass_instance)
@current_game_state = klass_instance
end
def previous_game_state
# current_game_state = @current_game_state
# @current_game_state = @last_frame_time
# @last_game_state = current_game_state
@last_game_state
end
# Sourced from https://gist.github.com/ippa/662583
def draw_circle(cx,cy,r, z = 9999,color = Gosu::Color::GREEN, step = 10)
0.step(360, step) do |a1|
a2 = a1 + step
draw_line(cx + Gosu.offset_x(a1, r), cy + Gosu.offset_y(a1, r), color, cx + Gosu.offset_x(a2, r), cy + Gosu.offset_y(a2, r), color, z)
end
end
end
end

View File

@@ -0,0 +1,302 @@
module CyberarmEngine
class GameObject
INSTANCES = []
IMAGES = {}
SAMPLES= {}
Vertex = Struct.new(:x, :y)
attr_accessor :image, :x, :y, :z, :angle, :center_x, :center_y, :scale_x, :scale_y,
:color, :alpha, :mode, :options, :paused, :radius, :last_x, :last_y
attr_reader :world_center_point
def initialize(options={})
if options[:auto_manage] || options[:auto_manage] == nil
INSTANCES.push(self)
$window.current_game_state.add_game_object(self)
end
@options = options
@image = options[:image] ? image(options[:image]) : nil
@x = options[:x] ? options[:x] : 0
@y = options[:y] ? options[:y] : 0
@z = options[:z] ? options[:z] : 0
@last_x = 0
@last_y = 0
@angle = options[:angle] ? options[:angle] : 0
@center_x = options[:center_x] ? options[:center_x] : 0.5
@center_y = options[:center_y] ? options[:center_y] : 0.5
@scale_x = options[:scale_x] ? options[:scale_x] : 1
@scale_y = options[:scale_y] ? options[:scale_y] : 1
@color = options[:color] ? options[:color] : Gosu::Color.argb(0xff_ffffff)
@alpha = options[:alpha] ? options[:alpha] : 255
@mode = options[:mode] ? options[:mode] : :default
@paused = false
@speed = 0
@debug_color = Gosu::Color::GREEN
@world_center_point = Vertex.new(0,0)
setup
@debug_text = MultiLineText.new("", color: @debug_color, y: self.y-(self.height*self.scale), z: 9999)
@debug_text.x = self.x
if @radius == 0 || @radius == nil
@radius = options[:radius] ? options[:radius] : defined?(@image.width) ? ((@image.width+@image.height)/4)*scale : 1
end
end
def draw
if @image
@image.draw_rot(@x, @y, @z, @angle, @center_x, @center_y, @scale_x, @scale_y, @color, @mode)
end
if $debug
show_debug_heading if $heading
$window.draw_circle(self.x, self.y, radius, 9999, @debug_color)
if @debug_text.text != ""
$window.draw_rect(@debug_text.x-10, (@debug_text.y-10), @debug_text.width+20, @debug_text.height+20, Gosu::Color.rgba(0,0,0,200), 9999)
@debug_text.draw
end
end
end
def update
end
def image(image_path)
image = nil
GameObject::IMAGES.detect do |img, instance|
if img == image_path
image = instance
true
end
end
unless image
instance = Gosu::Image.new(image_path)
GameObject::IMAGES[image_path] = instance
image = instance
end
return image
end
def sample(sample_path)
sample = nil
GameObject::SAMPLES.detect do |smp, instance|
if smp == sample_path
sample = instance
true
end
end
unless sample
instance = Gosu::Sample.new(sample_path)
GameObject::SAMPLES[sample_path] = instance
sample = instance
end
return sample
end
def debug_text(text)
@debug_text.text = text
end
def update_debug_text
@debug_text.x = self.x-(@debug_text.width/2)
@debug_text.y = self.y-((self.height)*self.scale)
end
def scale
if @scale_x == @scale_y
return @scale_x
else
false
# maths?
end
end
def scale=(int)
self.scale_x = int
self.scale_y = int
self.radius = ((@image.width+@image.height)/4)*self.scale
end
def x=(i)
@last_x = @x
@x = i
end
def y=(i)
@last_y = @y
@y = i
end
def visible
true
# if _x_visible
# if _y_visible
# true
# else
# false
# end
# else
# false
# end
end
def _x_visible
self.x.between?(($window.width/2)-(@world_center_point.x), ($window.width/2)+@world_center_point.x) ||
self.x.between?(((@world_center_point.x)-$window.width/2), ($window.width/2)+@world_center_point.x)
end
def _y_visible
self.y.between?(($window.height/2)-(@world_center_point.y), ($window.height/2)+@world_center_point.y) ||
self.y.between?((@world_center_point.y)-($window.height/2), ($window.height/2)+@world_center_point.y)
end
def heading(ahead_by = 100, object = nil, angle_only = false)
direction = ((Gosu.angle(@last_x, @last_y, self.x, self.y)) - 90.0) * (Math::PI / 180.0)
ahead_by+object.speed*Engine.dt if object
_x = @x+(ahead_by*Math.cos(direction))
_y = @y+(ahead_by*Math.sin(direction))
return direction if angle_only
return Vertex.new(_x, _y) unless angle_only
end
def show_debug_heading
_heading = heading
$window.draw_line(x, y, @debug_color, _heading.x, _heading.y, @debug_color, 9999)
end
def width
@image ? @image.width : 0
end
def height
@image ? @image.height : 0
end
def pause
@paused = true
end
def unpause
@paused = false
end
def rotate(int)
self.angle+=int
self.angle%=360
end
def alpha=int # 0-255
@alpha = int
@alpha = 255 if @alpha > 255
@color = Gosu::Color.rgba(@color.red, @color.green, @color.blue, int)
end
def draw_rect(x, y, width, height, color, z = 0)
$window.draw_rect(x,y,width,height,color,z)
end
def button_up(id)
end
def button_down?(id)
end
def find_closest(game_object_class)
best_object = nil
best_distance = 100_000_000_000 # Huge default number
game_object_class.all.each do |object|
distance = Gosu::distance(self.x, self.y, object.x, object.y)
if distance <= best_distance
best_object = object
best_distance = distance
end
end
return best_object
end
def look_at(object)
# TODO: Implement
end
def circle_collision?(object)
distance = Gosu.distance(self.x, self.y, object.x, object.y)
if distance <= self.radius+object.radius
true
else
false
end
end
# Duplication... so DRY.
def each_circle_collision(object, resolve_with = :width, &block)
if object.class != Class && object.instance_of?(object.class)
$window.current_game_state.game_objects.select {|i| i.class == object.class}.each do |o|
distance = Gosu.distance(self.x, self.y, object.x, object.y)
if distance <= self.radius+object.radius
block.call(o, object) if block
end
end
else
list = $window.current_game_state.game_objects.select {|i| i.class == object}
list.each do |o|
next if self == o
distance = Gosu.distance(self.x, self.y, o.x, o.y)
if distance <= self.radius+o.radius
block.call(self, o) if block
end
end
end
end
def destroy
INSTANCES.delete(self)
if $window.current_game_state
$window.current_game_state.game_objects.each do |o|
if o.is_a?(self.class) && o == self
$window.current_game_state.game_objects.delete(o)
end
end
end
end
# NOTE: This could be implemented more reliably
def self.all
INSTANCES.select {|i| i.class == self}
end
def self.each_circle_collision(object, resolve_with = :width, &block)
if object.class != Class && object.instance_of?(object.class)
$window.current_game_state.game_objects.select {|i| i.class == self}.each do |o|
distance = Gosu.distance(o.x, o.y, object.x, object.y)
if distance <= o.radius+object.radius
block.call(o, object) if block
end
end
else
lista = $window.current_game_state.game_objects.select {|i| i.class == self}
listb = $window.current_game_state.game_objects.select {|i| i.class == object}
lista.product(listb).each do |o, o2|
next if o == o2
distance = Gosu.distance(o.x, o.y, o2.x, o2.y)
if distance <= o.radius+o2.radius
block.call(o, o2) if block
end
end
end
end
def self.destroy_all
INSTANCES.clear
if $window.current_game_state
$window.current_game_state.game_objects.each do |o|
if o.is_a?(self.class)
$window.current_game_state.game_objects.delete(o)
end
end
end
end
end
end

View File

@@ -0,0 +1,64 @@
class GameState
SCALE_X_BASE = 1920.0
SCALE_Y_BASE = 1080.0
attr_accessor :options, :global_pause
attr_reader :game_objects
def initialize(options={})
$window.set_game_state(self)
@options = options unless @options
@game_objects = []
@global_pause = false
setup
@_4ship = Ship.all.first if Ship.all.is_a?(Array)
end
def setup
end
def draw
# count = 0
@game_objects.each do |o|
o.draw if o.visible
# p o.class if o.visible
# count+=1 if o.visible
end
# puts "Num visible objects: #{count} of #{@game_objects.count}"
end
def update
@game_objects.each do |o|
unless o.paused || @global_pause
o.world_center_point.x = @_4ship.x
o.world_center_point.y = @_4ship.y
o.update
o.update_debug_text if $debug
end
end
end
def destroy
@options = nil
@game_objects = nil
end
def button_up(id)
@game_objects.each do |o|
o.button_up(id) unless o.paused
end
end
def push_game_state(klass, options={})
$window.push_game_state(klass, options)
end
def draw_rect(x, y, width, height, color, z = 0)
$window.draw_rect(x,y,width,height,color,z)
end
def add_game_object(object)
@game_objects << object
end
end

View File

@@ -0,0 +1,59 @@
module CyberarmEngine
class MultiLineText
attr_accessor :options, :x, :y, :width, :height
def initialize(text, options={})
@texts = []
text.split("\n").each_with_index do |line, i|
_options = options
_options[:y]+=_options[:size]
@texts << Text.new(line, _options)
end
@options = options
@x = @texts.first ? @texts.first.x : 0
@y = @texts.first ? @texts.first.y : 0
@width = 0
@height = 0
calculate_boundry
end
def draw
@texts.each(&:draw)
end
def text
string = ""
@texts.each {|t| string << t.text}
return string
end
def text=(text)
text.split("\n").each_with_index do |line, i|
if @texts[i]
@texts[i].text = line
else
@texts << Text.new(line, @options)
end
end
calculate_boundry
end
def x=(int)
@x = int
@texts.each {|t| t.x = int}
end
def y=(int)
@y = int
@texts.each_with_index {|t, i| t.y=int+(i*t.size)}
end
def calculate_boundry
@width = 0
@height= 0
@texts.each {|t| @width = t.width if t.width > @width}
@texts.each {|t| @height+=t.height}
end
end
end

View File

@@ -0,0 +1,95 @@
module CyberarmEngine
class Text
CACHE = {}
attr_accessor :text, :x, :y, :z, :size, :factor_x, :factor_y, :color, :shadow, :shadow_size, :options
attr_reader :textobject
def initialize(text, options={})
@text = text || ""
@options = options
@size = options[:size] || 18
@font = options[:font] || Gosu.default_font_name
@x = options[:x] || 0
@y = options[:y] || 0
@z = options[:z] || 1025
@factor_x = options[:factor_x] || 1
@factor_y = options[:factor_y] || 1
@color = options[:color] || Gosu::Color::WHITE
@alignment= options[:alignment] || nil
@shadow = true if options[:shadow] == true
@shadow = false if options[:shadow] == false
@shadow = true if options[:shadow] == nil
@shadow_size = options[:shadow_size] ? options[:shadow_size] : 1
@shadow_alpha= options[:shadow_alpha] ? options[:shadow_alpha] : 30
@textobject = check_cache(@size, @font)
if @alignment
case @alignment
when :left
@x = 0+BUTTON_PADDING
when :center
@x = ($window.width/2)-(@textobject.text_width(@text)/2)
when :right
@x = $window.width-BUTTON_PADDING-@textobject.text_width(@text)
end
end
return self
end
def check_cache(size, font_name)
available = false
font = nil
if CACHE[size]
if CACHE[size][font_name]
font = CACHE[size][font_name]
available = true
else
available = false
end
else
available = false
end
unless available
font = Gosu::Font.new(@size, name: @font)
CACHE[@size] = {} unless CACHE[@size].is_a?(Hash)
CACHE[@size][@font] = font
end
return font
end
def width
textobject.text_width(@text)
end
def height
textobject.height
end
def draw
if @shadow && !ARGV.join.include?("--no-shadow")
_color = Gosu::Color.rgba(@color.red, @color.green, @color.blue, @shadow_alpha) if @shadow_alpha <= @color.alpha
_color = Gosu::Color.rgba(@color.red, @color.green, @color.blue, @color.alpha) unless @shadow_alpha <= @color.alpha
@textobject.draw(@text, @x-@shadow_size, @y, @z, @factor_x, @factor_y, _color)
@textobject.draw(@text, @x-@shadow_size, @y-@shadow_size, @z, @factor_x, @factor_y, _color)
@textobject.draw(@text, @x, @y-@shadow_size, @z, @factor_x, @factor_y, _color)
@textobject.draw(@text, @x+@shadow_size, @y-@shadow_size, @z, @factor_x, @factor_y, _color)
@textobject.draw(@text, @x, @y+@shadow_size, @z, @factor_x, @factor_y, _color)
@textobject.draw(@text, @x-@shadow_size, @y+@shadow_size, @z, @factor_x, @factor_y, _color)
@textobject.draw(@text, @x+@shadow_size, @y, @z, @factor_x, @factor_y, _color)
@textobject.draw(@text, @x+@shadow_size, @y+@shadow_size, @z, @factor_x, @factor_y, _color)
end
@textobject.draw(@text, @x, @y, @z, @factor_x, @factor_y, @color)
end
def update; end
end
end

View File

@@ -0,0 +1,122 @@
module CyberarmEngine
BUTTON_TEXT_COLOR = Gosu::Color::WHITE
BUTTON_TEXT_ACTIVE_COLOR = Gosu::Color::BLACK
BUTTON_COLOR = Gosu::Color.rgb(12,12,12)
BUTTON_HOVER_COLOR = Gosu::Color.rgb(100, 100, 100)
BUTTON_ACTIVE_COLOR = Gosu::Color.rgb(50, 50, 50)
BUTTON_TEXT_SIZE = 20
BUTTON_PADDING = 10
class Button
attr_accessor :text, :x, :y, :offset_x, :offset_y, :tooltip, :block
def initialize(text, x, y, auto_manage = true, tooltip = "", &block)
@text = Text.new(text, x: x, y: y, size: BUTTON_TEXT_SIZE, color: BUTTON_TEXT_COLOR, shadow: true)
@tooltip=Text.new(tooltip, x: x, y: y-(height/4*3), z: 10_000, size: BUTTON_TEXT_SIZE, color: BUTTON_TEXT_COLOR, shadow: false)
@x = x
@y = y
_x_ = @x+(@text.textobject.text_width(@text.text)/2)-(@tooltip.textobject.text_width(@tooltip.text)/2)
@tooltip.x = _x_+BUTTON_PADDING
auto_adjust_tooltip_position
@offset_x, @offset_y = 0, 0
if block
@block = Proc.new{yield(self)}
else
@block = Proc.new {}
end
Window.instance.elements.push(self) if auto_manage
return self
end
def update_position_toolip
_x_ = @x+(@text.textobject.text_width(@text.text)/2)-(@tooltip.textobject.text_width(@tooltip.text)/2)
@tooltip.x = _x_+BUTTON_PADDING
auto_adjust_tooltip_position
end
def auto_adjust_tooltip_position
if @tooltip.x <= 1
@tooltip.x = 2
elsif @tooltip.x+@tooltip.textobject.text_width(@tooltip.text) > $window.width-(BUTTON_PADDING+1)
@tooltip.x = $window.width-@tooltip.textobject.text_width(@tooltip.text)
end
end
def draw
@text.draw
$window.draw_rect(@x, @y, width, height, BUTTON_COLOR)
if mouse_clicked_on_check
$window.draw_rect(@x+1, @y+1, width-2, height-2, BUTTON_ACTIVE_COLOR)
elsif mouse_over?
$window.draw_rect(@x+1, @y+1, width-2, height-2, BUTTON_HOVER_COLOR)
show_tooltip
else
$window.draw_rect(@x+1, @y+1, width-2, height-2, BUTTON_COLOR)
end
end
def update
@text.x = @x+BUTTON_PADDING
@text.y = @y+BUTTON_PADDING
end
def button_up(id)
case id
when Gosu::MsLeft
click_check
end
end
def click_check
if mouse_over?
puts "Clicked: #{@text.text}"
@block.call if @block.is_a?(Proc)
end
end
def mouse_clicked_on_check
if mouse_over? && Gosu.button_down?(Gosu::MsLeft)
true
end
end
def mouse_over?
if $window.mouse_x.between?(@x+@offset_x, @x+@offset_x+width)
if $window.mouse_y.between?(@y+@offset_y, @y+@offset_y+height)
true
end
end
end
def show_tooltip
if @tooltip.text != ""
x = @tooltip.x-BUTTON_PADDING
$window.draw_rect(x, @y-height, width(@tooltip), height(@tooltip), BUTTON_ACTIVE_COLOR, 9_999)
$window.draw_rect(x-1, @y-height-1, width(@tooltip)+2, height(@tooltip)+2, Gosu::Color::WHITE, 9_998)
@tooltip.draw
end
end
def width(text_object = @text)
text_object.textobject.text_width(text_object.text)+BUTTON_PADDING*2
end
def height(text_object = @text)
text_object.textobject.height+BUTTON_PADDING*2
end
def set_offset(x, y)
@offset_x, @offset_y = x, y
end
def update_text(string)
@text.text = string
end
end
end

View File

@@ -0,0 +1,73 @@
module CyberarmEngine
class CheckBox
SIZE = 22
attr_accessor :x, :y, :checked
attr_reader :text
def initialize(x, y, checked = false, size = CheckBox::SIZE)
@x, @y = x, y
@checked = checked
@size = size
@text = Text.new("", false, x: x, y: y, size: size, color: BUTTON_TEXT_COLOR, shadow: true)
return self
end
def x=(int)
@x = int
@text.x = int
end
def y=(int)
@y = int
@text.y = int
end
def draw
$window.draw_rect(@x, @y, width, height, Gosu::Color::BLACK)
if mouse_over?
$window.draw_rect(@x+1, @y+1, width-2, height-2, BUTTON_HOVER_COLOR)
else
if @checked
$window.draw_rect(@x+1, @y+1, width-2, height-2, BUTTON_ACTIVE_COLOR)
else
$window.draw_rect(@x+1, @y+1, width-2, height-2, BUTTON_COLOR)
end
end
if @checked
@text.draw
end
end
def update
@text.x = @x+BUTTON_PADDING
@text.y = @y+BUTTON_PADDING
end
def button_up(id)
if mouse_over? && id == Gosu::MsLeft
if @checked
@checked = false
else
@checked = true
end
end
end
def mouse_over?
if $window.mouse.x.between?(@x, @x+width)
if $window.mouse.y.between?(@y, @y+height)
true
end
end
end
def width(text_object = @text)
text_object.textobject.text_width(text_object.text)+BUTTON_PADDING*2
end
def height(text_object = @text)
text_object.textobject.height+BUTTON_PADDING*2
end
end
end

View File

@@ -0,0 +1,157 @@
module CyberarmEngine
class Container
attr_accessor :text_color
attr_reader :elements, :x, :y, :width, :height, :options
attr_reader :scroll_x, :scroll_y, :internal_width, :internal_height
def initialize(x = 0, y = 100, width = $window.width, height = $window.height, options = {})
@x, @y, @width, @height, @internal_width, @internal_height = x, y, width, height-y, width, height-y
@scroll_x, @scroll_y = 0, 0
@scroll_speed = 10
puts "#{self.class}: width #{width}, height #{@height}"
@options = {}
@allow_recreation_on_resize = true
@text_color = options[:text_color] || Gosu::Color::WHITE
@elements = []
if defined?(self.setup); setup; end
end
def draw
Gosu.clip_to(x, y, width, height) do
Gosu.translate(scroll_x, scroll_y) do
@elements.each(&:draw)
end
end
end
def update
@elements.each(&:update)
end
def button_up(id)
if $window.mouse_x.between?(@x, @x+@width)
if $window.mouse_y.between?(@y, @y+@height)
case id
when Gosu::MsWheelUp
@scroll_y+=@scroll_speed
@scroll_y = 0 if @scroll_y > 0
@elements.each {|e| e.set_offset(@scroll_x, @scroll_y) if e.is_a?(Button) }
when Gosu::MsWheelDown
@scroll_y-=@scroll_speed
if $window.height-@internal_height-y > 0
@scroll_y = 0
p "H: #{@height-@internal_height}", "Y: #{@scroll_y}"
else
@scroll_y = @height-@internal_height if @scroll_y <= @height-@internal_height
end
@elements.each {|e| e.set_offset(@scroll_x, @scroll_y) if e.is_a?(Button) }
end
end
end
@elements.each {|e| if defined?(e.button_up); e.button_up(id); end}
end
def build(&block)
yield(self)
end
def text(text, x, y, size = 18, color = self.text_color, alignment = nil, font = nil)
relative_x = @x+x
relative_y = @y+y
_text = Text.new(text, x: relative_x, y: relative_y, size: size, color: color, alignment: alignment, font: font)
@elements.push(_text)
if _text.y-(_text.height*2) > @internal_height
@internal_height+=_text.height
end
return _text
end
def button(text, x, y, tooltip = "", &block)
relative_x = @x+x
relative_y = @y+y
_button = Button.new(text, relative_x, relative_y, false, tooltip) { if block.is_a?(Proc); block.call; end }
@elements.push(_button)
if _button.y-(_button.height*2) > @internal_height
@internal_height+=_button.height
end
return _button
end
def input(text, x, y, width = Input::WIDTH, size = 18, color = Gosu::Color::BLACK, tooltip = "")
relative_x = @x+x
relative_y = @y+y
_input = Input.new(text, relative_x, relative_y, width, size, color)
@elements.push(_input)
if _input.y-(_input.height*2) > @internal_height
@internal_height+=_input.height
end
return _input
end
def check_box(x, y, checked = false, size = CheckBox::SIZE)
relative_x = @x+x
relative_y = @y+y
_check_box = CheckBox.new(relative_x, relative_y, checked, size)
@elements.push(_check_box)
if _check_box.y-(_check_box.height*2) > @internal_height
@internal_height+=_check_box.height
end
return _check_box
end
def resize
if @allow_recreation_on_resize
$window.active_container = self.class.new
end
end
# Fills container background with color
def fill(color = Gosu::Color::BLACK, z = 0)
$window.draw_rect(@x, @y, @width, @height, color, z)
end
def set_layout_y(start, spacing)
@layout_y_start = start
@layout_y_spacing = spacing
@layout_y_count = 0
end
def layout_y(stay = false)
i = @layout_y_start+(@layout_y_spacing*@layout_y_count)
@layout_y_count+=1 unless stay
return i
end
# Return X position relative to container
def relative_x(int)
int-self.x
end
# Return Y position relative to container
def relative_y(int)
int-self.y
end
def calc_percentage(positive, total)
begin
i = ((positive.to_f/total.to_f)*100.0).round(2)
if !i.nan?
return "#{i}%"
else
"N/A"
end
rescue ZeroDivisionError => e
puts e
return "N/A" # 0 / 0, safe to assume no actionable data
end
end
end
end

View File

@@ -0,0 +1,117 @@
module CyberarmEngine
class Input
WIDTH = 200
FOCUS_BACKGROUND_COLOR = Gosu::Color.rgb(150,150,144)
NO_FOCUS_BACKGROUND_COLOR = Gosu::Color.rgb(130,130,130)
attr_accessor :text, :x, :y, :width, :size, :color, :type, :focus
attr_reader :text_object, :text_input, :height, :fixed_x
def initialize(text, x, y, width = WIDTH, size = Text::SIZE, color = Gosu::Color::BLACK, tooltip = "", type = nil)
@text = text
@x, @y= x, y
@width= width
@size = size
@color= color
@tooltip=tooltip
@type = type
@focus = false
@text_object = Text.new(text, x: x, y: y, size: size, color: color, shadow: true)
@height = @text_object.height
@text_input = Gosu::TextInput.new
@text_input.text = @text
@background_color = NO_FOCUS_BACKGROUND_COLOR
@fixed_x = @x
@x_offset= 0
@carot_ticks = 0
@carot_width = 2.5
@carot_height= @text_object.height
@carot_color = Gosu::Color.rgb(50,50,25)
@carot_show_ticks = 25
@show_carot = true
return self
end
def text=(string)
@text = string
@text_input.text, @text_object.text = @text, @text
end
def draw
$window.draw_rect(x, y, width, height, Gosu::Color::BLACK)
$window.draw_rect(x+1, y+1, width-2, height-2, @background_color)
Gosu.clip_to(x, @text_object.y, width, @text_object.height) do
@text_object.draw
# Carot (Cursor)
$window.draw_rect((@x+@text_object.width)-@x_offset, @text_object.y, @carot_width, @carot_height, @carot_color) if @show_carot && @focus
end
end
def update
@text_object.y = @y+BUTTON_PADDING
if (@text_object.width+@carot_width)-@width >= 0
@x_offset = (@text_object.width+@carot_width)-@width
else
@x_offset = 0
end
@text = @text_object.text
@carot_ticks+=1
if @carot_ticks >= @carot_show_ticks
if @show_carot
@show_carot = false
else
@show_carot = true
end
@carot_ticks = 0
end
if @focus
@text_object.text = @text_input.text
$window.text_input = @text_input unless $window.text_input == @text_input
end
if mouse_over? && $window.button_down?(Gosu::MsLeft)
@focus = true
@background_color = FOCUS_BACKGROUND_COLOR
end
if !mouse_over? && $window.button_down?(Gosu::MsLeft)
@focus = false
$window.text_input = nil
@background_color = NO_FOCUS_BACKGROUND_COLOR
end
if @text_object.width >= @width
@text_object.x = self.fixed_x-@x_offset
else
@text_object.x = self.fixed_x
end
end
def mouse_over?
if $window.mouse_x.between?(@x, @x+width)
if $window.mouse_y.between?(@y, @y+height)
true
end
end
end
def width(text_object = @text_object)
# text_object.textobject.text_width(text_object.text)+BUTTON_PADDING*2
@width
end
def height(text_object = @text_object)
text_object.textobject.height+BUTTON_PADDING*2
end
end
end

View File

@@ -0,0 +1,3 @@
module CyberarmEngine
VERSION = "0.1.0"
end

View File

@@ -0,0 +1,11 @@
require "test_helper"
class CyberarmEngineTest < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::CyberarmEngine::VERSION
end
def test_it_does_something_useful
assert false
end
end

4
test/test_helper.rb Normal file
View File

@@ -0,0 +1,4 @@
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
require "cyberarm_engine"
require "minitest/autorun"