fantasy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 98740063fd63398988bf5b11e625a75e2526f617ab22e9aa1ff483a65c0f91cb
4
+ data.tar.gz: b4eecd987e01f24f2a1d3aef7627c195992ec0ffa0c4b124fb6ca577896a2a58
5
+ SHA512:
6
+ metadata.gz: ab99759553de96490612fbf1c6089db1113ec26969876b88d9215fbb1317a7ee08dfef98cd376c7843750013ed3981b10fc0beba0e816a6c5c7358511166b023
7
+ data.tar.gz: 69c41125931104d3440c02bfe1073b6d37f27d2efebbe3fdd331165c014739bf81f92d80c83c46f636d0eda77d583a802a7f377c943ce8964a8bf9d7567f7d4d
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-03-11
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ ruby ">= 3.0.0"
6
+
7
+ # Specify your gem's dependencies in fantasy.gemspec
8
+ gemspec
9
+
10
+ gem "rake", "~> 13.0"
11
+ gem "minitest", "~> 5.0"
12
+ gem "rubocop", "~> 1.21"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Fernando Guillen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # Ruby in Fantasy
2
+
3
+ An upper layer over Gosu library to offer a more friendly API for an easy and lean game development.
4
+
5
+ Specially intended to use Ruby as a learning language to introduce children into programming.
6
+
7
+ **Attention**: This project is in early development phase, right now it is just an experiment
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem "fantasy"
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle install
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install fantasy
24
+
25
+ ## Features
26
+
27
+ ### Actor
28
+
29
+ Managing game elements which have (optionally) image, movement and collision
30
+
31
+ - Easy to set an `image`
32
+ - Managing movement through `direction` and `speed`
33
+ - Built-in movement control through cursors
34
+ - Collision detection, OnCollision callback
35
+ - Jump (TODO)
36
+ - Gravity (TODO)
37
+ - Animations (TODO)
38
+ - Possibility to extend Actor class or instantiate it directly for simple characters
39
+
40
+ ### Clock
41
+
42
+ Create new execution threads to program delayed behaviors, repetitive actions, animations, ...
43
+
44
+ - Run now
45
+ - Run after a delay
46
+ - Repeat
47
+ - Stop
48
+ - Callback when finished (TODO)
49
+
50
+ ### Animations
51
+
52
+ Add animations to an Actor and activate them when special state triggered
53
+
54
+ - Set frames (TODO)
55
+ - Duration in seconds (TODO)
56
+ - Run once (TODO)
57
+ - Run on loop (TODO)
58
+ - Stop (TODO)
59
+ - Callback when finished (TODO)
60
+
61
+ ### UI
62
+
63
+ #### Button (TODO)
64
+
65
+ Easy to set up a graphical component that responds to mouse hover and click events.
66
+
67
+ - Set image (TODO)
68
+ - Set image when hover (TODO)
69
+ - Set image when clicked (TODO)
70
+ - OnClicked callback (TODO)
71
+
72
+ #### HUD Elements
73
+
74
+ For easy creation of head-up display components.
75
+
76
+ - Text Element
77
+ - Image Element
78
+ - Auto update text (observer variable changes) (TODO)
79
+
80
+ ### Debug Mode
81
+
82
+ When active different attributes from all the Actors and HUD elements will be visible in the screen.
83
+
84
+ - Position
85
+ - Collider
86
+
87
+ On top, when debug mode is active, the Actors and HUD elements become draggable.
88
+ The new position won't be persistent but it will be helpful to identify which should be the desired position.
89
+
90
+ ### Camera
91
+
92
+ Setting up a camera component that we can easily move around and all the active
93
+ Actors in the game will be rendered in the relative position to this camera.
94
+
95
+ - Allow camera move
96
+ - Easy to follow one actor
97
+
98
+ ### Colors palette (TODO)
99
+
100
+ - Not deal with RGB or anything, just a list of colors (TODO)
101
+
102
+ ### Game Scene transitions
103
+
104
+ Easy to configure 3 basic game states:
105
+
106
+ - Initial screen
107
+ - Game
108
+ - Game over screen
109
+
110
+ Each state should be independent and unique Actors and other elements can be created and configured for each state
111
+
112
+ Built-in mechanism to move from one state to another.
113
+
114
+ ### Pause Game (TODO)
115
+
116
+ - Pause game, pause Actors, animations, clocks, ... (TODO)
117
+
118
+ ### Core run
119
+
120
+ Move the core functions to the top level hierarchy so I don't need to create a `Game::Window` class
121
+
122
+ ### Sound
123
+
124
+ Direct and easy way to play a sound
125
+
126
+ ### Data Persistance (TODO)
127
+
128
+ Simple mechanismo to save data in disk. For user preferences, game progress, high scores and others
129
+
130
+ ### User Inputs
131
+
132
+ Easy access to keyboard and mouse inputs on any part of the code. Specially in the Actors.
133
+
134
+ - Allow "on_space_bar" and each Actor (TODO)
135
+ - Allow multiple "on_space_bar" (TODO)
136
+ - Remove "on_space_bar" when changing scene (TODO)
137
+
138
+ ## API
139
+
140
+
141
+
142
+ ## Credits for assets
143
+
144
+ - Sprites: www.kenney.nl
145
+ - Font: VT323 Project Authors (peter.hull@oikoi.com)
146
+
147
+ ## Bugs
148
+
149
+ - When dragging in debug mode new elements are being added to the drag (TODO)
150
+
151
+ ## Development
152
+
153
+ 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.
154
+
155
+ 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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
156
+
157
+ ## Contributing
158
+
159
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fguillen/fantasy.
160
+
161
+ ## License
162
+
163
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ require "rubocop/rake_task"
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ task default: %i[test rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "fantasy"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/fantasy.gemspec ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/fantasy/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fantasy"
7
+ spec.version = Fantasy::VERSION
8
+ spec.authors = ["Fernando Guillen"]
9
+ spec.email = ["fguillen.mail@gmail.com"]
10
+
11
+ spec.summary = "Simple toolbox library and lean API to build great mini games"
12
+ spec.description = "Simple toolbox library and lean API to build great mini games"
13
+ spec.homepage = "https://github.com/fguillen/fantasy"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.0.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/fguillen/fantasy"
21
+ spec.metadata["changelog_uri"] = "https://github.com/fguillen/fantasy/blob/main/CHANGELOG.md"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ # Uncomment to register a new dependency of your gem
35
+ spec.add_dependency "gosu", "~> 1.4.1"
36
+ spec.add_dependency "vector2d", "~> 2.2.3"
37
+
38
+ # For more information and examples about making a new gem, checkout our
39
+ # guide at: https://bundler.io/guides/creating_gem.html
40
+ end
data/fonts/OFL.txt ADDED
@@ -0,0 +1,93 @@
1
+ Copyright 2011, The VT323 Project Authors (peter.hull@oikoi.com)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file
@@ -0,0 +1,141 @@
1
+ class Actor
2
+ attr_reader :image, :moving_with_cursors
3
+ attr_accessor :position, :direction, :speed, :solid, :scale, :name, :layer
4
+
5
+ def initialize(image_name)
6
+ @image = Gosu::Image.new("#{__dir__}/../images/#{image_name}.png", { retro: true })
7
+ @name = image_name
8
+ @position = Coordinates.new(0, 0)
9
+ @direction = Coordinates.new(0, 0)
10
+ @speed = 0
11
+ @scale = 1
12
+ @on_after_move_callback = nil
13
+ @moving_with_cursors = false
14
+ @solid = false
15
+ @draggable_on_debug = true
16
+ @dragging = false
17
+ @dragging_offset = nil
18
+ @layer = 0
19
+
20
+ @on_collision_callback = nil
21
+
22
+ Global.actors << self
23
+ end
24
+
25
+ def image=(image_name)
26
+ @image = Gosu::Image.new("#{__dir__}/../images/#{image_name}.png", { retro: true })
27
+ end
28
+
29
+ def width
30
+ @image.width() * @scale
31
+ end
32
+
33
+ def height
34
+ @image.height() * @scale
35
+ end
36
+
37
+ def move_with_cursors
38
+ @moving_with_cursors = true
39
+ end
40
+
41
+ def direction=(value)
42
+ @direction = value
43
+ @direction = @direction.normalize
44
+ end
45
+
46
+ def draw
47
+ @image.draw(position_in_camera.x, position_in_camera.y, 0, @scale, @scale)
48
+
49
+ draw_debug if Global.debug
50
+ end
51
+
52
+ def draw_debug
53
+ Utils.draw_frame(position_in_camera.x, position_in_camera.y, width, height, 1, Gosu::Color::RED) if solid
54
+ Global.pixel_font.draw_text("#{@position.x.floor},#{@position.y.floor}", position_in_camera.x, position_in_camera.y - 20, 1)
55
+ end
56
+
57
+ def position_in_camera
58
+ @position - Global.camera.position
59
+ end
60
+
61
+ def move
62
+ mouse_position = Global.mouse_position + Global.camera.position
63
+
64
+ if @draggable_on_debug && Global.debug && !@dragging && Gosu.button_down?(Gosu::MS_LEFT) && Utils.collision_at?(self, mouse_position.x, mouse_position.y)
65
+ @dragging = true
66
+ @dragging_offset = mouse_position - @position
67
+ end
68
+
69
+ if @dragging && !Gosu.button_down?(Gosu::MS_LEFT)
70
+ @dragging = false
71
+ end
72
+
73
+ if @dragging
74
+ @position = mouse_position - @dragging_offset
75
+ else
76
+ calculate_direction_by_cursors if @moving_with_cursors
77
+
78
+ if @direction != Coordinates.zero && !@speed.zero?
79
+ @last_position = @position
80
+ @position = @position + (@direction * @speed * Global.frame_time)
81
+
82
+ if solid?
83
+ actual_collisions = collisions
84
+ actual_collisions.each do |actor|
85
+ collision_with(actor)
86
+ actor.collision_with(self)
87
+ end
88
+
89
+ @position = @last_position if actual_collisions.any?
90
+ end
91
+ end
92
+ end
93
+
94
+ @on_after_move_callback.call unless @on_after_move_callback.nil?
95
+ end
96
+
97
+ def solid?
98
+ @solid
99
+ end
100
+
101
+ def on_after_move(&block)
102
+ @on_after_move_callback = block
103
+ end
104
+
105
+ def on_collision(&block)
106
+ @on_collision_callback = block
107
+ end
108
+
109
+ def on_destroy(&block)
110
+ @on_destroy_callback = block
111
+ end
112
+
113
+ def collision_with(actor)
114
+ @on_collision_callback.call(actor) unless @on_collision_callback.nil?
115
+ end
116
+
117
+ def calculate_direction_by_cursors
118
+ if Gosu.button_down?(Gosu::KB_DOWN)
119
+ @direction = Coordinates.down
120
+ elsif Gosu.button_down?(Gosu::KB_UP)
121
+ @direction = Coordinates.up
122
+ elsif Gosu.button_down?(Gosu::KB_RIGHT)
123
+ @direction = Coordinates.right
124
+ elsif Gosu.button_down?(Gosu::KB_LEFT)
125
+ @direction = Coordinates.left
126
+ else
127
+ @direction = Coordinates.zero
128
+ end
129
+ end
130
+
131
+ def collisions
132
+ Global.actors.reject { |e| e == self }.select { |e| e.solid? }.select do |actor|
133
+ Utils.collision? self, actor
134
+ end
135
+ end
136
+
137
+ def destroy
138
+ @on_destroy_callback.call unless @on_destroy_callback.nil?
139
+ Global.actors.delete(self)
140
+ end
141
+ end
@@ -0,0 +1,59 @@
1
+ Global.setup_proc = nil
2
+ Global.loop_proc = nil
3
+
4
+ def on_presentation(&block)
5
+ Global.presentation_proc = block
6
+ end
7
+
8
+ def on_game(&block)
9
+ Global.game_proc = block
10
+ end
11
+
12
+ def on_end(&block)
13
+ Global.end_proc = block
14
+ end
15
+
16
+ def on_setup(&block)
17
+ Global.setup_proc = block
18
+ end
19
+
20
+ def on_loop(&block)
21
+ Global.loop_proc = block
22
+ end
23
+
24
+ def on_button(&block)
25
+ Global.button_proc = block
26
+ end
27
+
28
+ def on_space_bar(&block)
29
+ Global.space_bar_proc = block
30
+ end
31
+
32
+ def on_cursor_up(&block)
33
+ Global.cursor_up_proc = block
34
+ end
35
+
36
+ def on_cursor_down(&block)
37
+ Global.cursor_down_proc = block
38
+ end
39
+
40
+ def on_cursor_left(&block)
41
+ Global.cursor_left_proc = block
42
+ end
43
+
44
+ def on_cursor_right(&block)
45
+ Global.cursor_right_proc = block
46
+ end
47
+
48
+ def on_mouse_button_left(&block)
49
+ Global.mouse_button_left_proc = block
50
+ end
51
+
52
+ def on_mouse_button_right(&block)
53
+ Global.mouse_button_right_proc = block
54
+ end
55
+
56
+ def start!
57
+ Global.game = Game.new
58
+ Global.game.show
59
+ end
@@ -0,0 +1,7 @@
1
+ class Camera
2
+ attr_accessor :position
3
+
4
+ def initialize(position: Coordinates.zero)
5
+ @position = position
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ class Clock
2
+ def initialize(&block)
3
+ @block = block
4
+ @thread = nil
5
+
6
+ Global.clocks << self
7
+ end
8
+
9
+ def run_now
10
+ @thread =
11
+ Thread.new do
12
+ @block.call
13
+ end
14
+ end
15
+
16
+ def run_on(seconds:)
17
+ @thread =
18
+ Thread.new do
19
+ sleep(seconds)
20
+ @block.call
21
+ end
22
+ end
23
+
24
+ def repeat(seconds: 1, times: Float::INFINITY)
25
+ times_executed = 0
26
+ @thread =
27
+ Thread.new do
28
+ while(times_executed < times)
29
+ @block.call
30
+ times_executed += 1;
31
+ sleep(seconds)
32
+ end
33
+ end
34
+ end
35
+
36
+ def stop
37
+ Thread.kill(@thread)
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ class Color < Gosu::Color
2
+ def initialize(r:, g:, b:, a: 255)
3
+ super(a, r, g, b)
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ require "vector2d"
2
+
3
+ class Coordinates < Vector2d
4
+ def self.zero
5
+ Coordinates.new(0, 0)
6
+ end
7
+
8
+ def self.up
9
+ Coordinates.new(0, -1)
10
+ end
11
+
12
+ def self.down
13
+ Coordinates.new(0, 1)
14
+ end
15
+
16
+ def self.left
17
+ Coordinates.new(-1, 0)
18
+ end
19
+
20
+ def self.right
21
+ Coordinates.new(1, 0)
22
+ end
23
+
24
+ def x=(value)
25
+ @x = value
26
+ end
27
+
28
+ def y=(value)
29
+ @y = value
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ module Draggable
2
+ def drag
3
+ puts "XXX: dragging 1 #{name}"
4
+ mouse_position = Global.mouse_position
5
+
6
+ puts "@draggable_on_debug: #{@draggable_on_debug}"
7
+ puts "@dragging: #{@dragging}"
8
+ puts "Gosu.button_down?(Gosu::MS_LEFT): #{Gosu.button_down?(Gosu::MS_LEFT)}"
9
+ puts "collision: #{Utils.collision_at?(self, mouse_position.x, mouse_position.y)}"
10
+
11
+ if @draggable_on_debug && !@dragging && Gosu.button_down?(Gosu::MS_LEFT) && Utils.collision_at?(self, mouse_position.x, mouse_position.y)
12
+ puts "XXX: dragging start #{name}"
13
+ @dragging = true
14
+ @dragging_offset = mouse_position - @position
15
+ end
16
+
17
+ if @dragging && !Gosu.button_down?(Gosu::MS_LEFT)
18
+ puts "XXX: dragging end #{name}"
19
+ @dragging = false
20
+ end
21
+
22
+ if @dragging
23
+ puts "XXX: dragging 3 #{name}"
24
+ @position = mouse_position - @dragging_offset
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,106 @@
1
+ require "ostruct"
2
+
3
+ module Global
4
+ class << self
5
+ attr_accessor :actors, :hud_texts, :hud_images, :clocks
6
+ attr_accessor :debug
7
+ attr_accessor :setup_proc, :loop_proc, :button_proc
8
+ attr_accessor :presentation_proc, :game_proc, :end_proc
9
+
10
+ attr_accessor :space_bar_proc
11
+ attr_accessor :cursor_up_proc, :cursor_down_proc, :cursor_left_proc, :cursor_right_proc
12
+ attr_accessor :mouse_button_left_proc, :mouse_button_right_proc
13
+
14
+ attr_accessor :background
15
+
16
+ attr_accessor :game
17
+ attr_reader :frame_time # delta_time
18
+ attr_reader :pixel_font
19
+ attr_reader :references
20
+ attr_reader :camera
21
+ attr_reader :game_state
22
+
23
+ def initialize
24
+ puts "Global.initialize"
25
+ @actors = []
26
+ @hud_texts = []
27
+ @hud_images = []
28
+ @clocks = []
29
+ @last_frame_at = Time.now
30
+ @debug = false
31
+ @pixel_font = Gosu::Font.new(20, { name: "#{__dir__}/../fonts/VT323-Regular.ttf" } )
32
+ @d_key_pressed = false
33
+ @references = OpenStruct.new
34
+ @camera = Camera.new(position: Coordinates.zero)
35
+ @game_state = Global.presentation_proc.nil? ? "game" : "presentation"
36
+
37
+ @presentation_proc = Global.default_on_presentation if @presentation_proc.nil?
38
+ @game_proc = Global.default_on_game if @game_proc.nil?
39
+ @end_proc = Global.default_on_end if @end_proc.nil?
40
+ @paused = false
41
+ end
42
+
43
+ def update
44
+ @frame_time = Time.now - @last_frame_at
45
+ @last_frame_at = Time.now
46
+
47
+ if Gosu.button_down?(Gosu::KB_D) && !@d_key_pressed
48
+ @debug = !@debug
49
+ @d_key_pressed = true
50
+ end
51
+
52
+ if !Gosu.button_down?(Gosu::KB_D) && @d_key_pressed
53
+ @d_key_pressed = false
54
+ end
55
+ end
56
+
57
+ def add_reference(name:, object:)
58
+ @references[name] = object
59
+ end
60
+
61
+ def default_on_presentation
62
+ Global.go_to_game
63
+ end
64
+
65
+ def default_on_game
66
+ raise "You have to define a 'on_game' block"
67
+ end
68
+
69
+ def default_on_end
70
+ Global.go_to_presentation
71
+ end
72
+
73
+ def go_to_presentation
74
+ puts "Game stage 'presentation'"
75
+
76
+ clear_state_elements
77
+ presentation_proc.call
78
+ end
79
+
80
+ def go_to_game
81
+ puts "Game stage 'game'"
82
+
83
+ clear_state_elements
84
+ game_proc.call
85
+ end
86
+
87
+ def go_to_end
88
+ puts "Game stage 'end'"
89
+
90
+ clear_state_elements
91
+ end_proc.call
92
+ end
93
+
94
+ def clear_state_elements
95
+ @actors.clear
96
+ @hud_texts.clear
97
+ @hud_images.clear
98
+ @clocks.each(&:stop)
99
+ @background = Color.new(r: 0, g: 0, b: 0)
100
+ end
101
+
102
+ def mouse_position
103
+ Coordinates.new(Global.game.mouse_x, Global.game.mouse_y)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,46 @@
1
+ class HudImage
2
+ include Draggable
3
+
4
+ attr_accessor :name, :scale, :color, :visible, :position, :layer
5
+
6
+ def initialize(position:, image_name: )
7
+ @image = Gosu::Image.new("#{__dir__}/../images/#{image_name}.png", { retro: true })
8
+ @name = image_name
9
+ @position = position
10
+ @scale = 1
11
+ @visible = true
12
+ @draggable_on_debug = true
13
+ @dragging = false
14
+ @layer = 100
15
+
16
+ Global.hud_images.push(self)
17
+ end
18
+
19
+ def width
20
+ @image.width() * @scale
21
+ end
22
+
23
+ def height
24
+ @image.height() * @scale
25
+ end
26
+
27
+ def move
28
+ drag if Global.debug
29
+ end
30
+
31
+ def draw
32
+ if visible
33
+ @image.draw(@position.x, @position.y, 0, @scale, @scale)
34
+ end
35
+
36
+ draw_debug if Global.debug
37
+ end
38
+
39
+ def draw_debug
40
+ Global.pixel_font.draw_text("#{@position.x.floor},#{@position.y.floor}", @position.x, @position.y - 20, 1)
41
+ end
42
+
43
+ def destroy
44
+ Global.hud_images.delete(self)
45
+ end
46
+ end
@@ -0,0 +1,57 @@
1
+ class HudText
2
+ attr_accessor :text, :size, :color, :visible, :layer, :in_world
3
+
4
+ def initialize(position:, text: "")
5
+ @position = position
6
+ @text = text
7
+ @size = "medium"
8
+ @color = Gosu::Color::WHITE
9
+ @visible = true
10
+ @layer = 100
11
+ @in_world = false
12
+
13
+ Global.hud_texts.push(self)
14
+ end
15
+
16
+ def move; end
17
+
18
+ def draw
19
+ if visible
20
+ Global.pixel_font.draw_markup(text, screen_position.x + scale, screen_position.y - 20 + scale, 1, scale, scale, Gosu::Color::BLACK)
21
+ Global.pixel_font.draw_markup(text, screen_position.x, screen_position.y - 20, 1, scale, scale, color)
22
+ end
23
+
24
+ draw_debug if Global.debug
25
+ end
26
+
27
+ def scale
28
+ case @size
29
+ when "small"
30
+ 1
31
+ when "medium"
32
+ 2
33
+ when "big"
34
+ 3
35
+ when "huge"
36
+ 4
37
+ else
38
+ raise "HudText.size not valid '#{@size}'. Valid sizes: 'small, medium, big, huge'"
39
+ end
40
+ end
41
+
42
+ def screen_position
43
+ if @in_world
44
+ @position - Global.camera.position
45
+ else
46
+ @position
47
+ end
48
+ end
49
+
50
+ def destroy
51
+ Global.hud_texts.delete(self)
52
+ end
53
+
54
+ def draw_debug
55
+ Global.pixel_font.draw_text("#{@position.x.floor},#{@position.y.floor}", @position.x, @position.y, 1)
56
+ end
57
+ end
@@ -0,0 +1,55 @@
1
+ class Game < Gosu::Window
2
+ def initialize
3
+ # TODO: require SCREEN_WIDTH and SCREEN_HEIGHT
4
+ super(SCREEN_WIDTH, SCREEN_HEIGHT)
5
+ Global.initialize
6
+
7
+ Global.presentation_proc.call()
8
+ end
9
+
10
+ def button_down(button_id)
11
+ case button_id
12
+ when Gosu::KB_DOWN then Global.cursor_down_proc.call unless Global.cursor_down_proc.nil?
13
+ when Gosu::KB_UP then Global.cursor_up_proc.call unless Global.cursor_up_proc.nil?
14
+ when Gosu::KB_LEFT then Global.cursor_left_proc.call unless Global.cursor_left_proc.nil?
15
+ when Gosu::KB_RIGHT then Global.cursor_right_proc.call unless Global.cursor_right_proc.nil?
16
+ when Gosu::MS_LEFT then Global.mouse_button_left_proc.call unless Global.mouse_button_left_proc.nil?
17
+ when Gosu::MS_RIGHT then Global.mouse_button_right_proc.call unless Global.mouse_button_right_proc.nil?
18
+ when Gosu::KB_SPACE then Global.space_bar_proc.call unless Global.space_bar_proc.nil?
19
+ end
20
+
21
+ Global.button_proc.call(button_id) unless Global.button_proc.nil?
22
+
23
+ super
24
+ end
25
+
26
+ def update
27
+ Global.update
28
+
29
+ Global.actors.each do |e|
30
+ e.move
31
+ end
32
+
33
+ Global.hud_texts.each do |e|
34
+ e.move
35
+ end
36
+
37
+ Global.hud_images.each do |e|
38
+ e.move
39
+ end
40
+
41
+ Global.loop_proc.call() unless Global.loop_proc.nil?
42
+ end
43
+
44
+ def draw
45
+ Gosu.draw_rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, Global.background)
46
+
47
+ (
48
+ Global.actors +
49
+ Global.hud_texts +
50
+ Global.hud_images
51
+ ).sort_by(&:layer).each do |e|
52
+ e.draw
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ module Sound
2
+ class << self
3
+ @@sounds = {}
4
+
5
+ def play(sound_name)
6
+ locate_sound(sound_name).play
7
+ end
8
+
9
+ def locate_sound(sound_name)
10
+ return @@sounds[sound_name] if @@sounds[sound_name]
11
+
12
+ puts "Initialize Sound: '#{sound_name}'"
13
+
14
+ base_path = "#{__dir__}/../sounds"
15
+ file_name = Dir.entries(base_path).find { |e| e.start_with?(sound_name) }
16
+ @@sounds[sound_name] = Gosu::Sample.new("#{base_path}/#{file_name}")
17
+
18
+ return @@sounds[sound_name]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ module Utils
2
+ # https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
3
+ def self.collision?(actor_1, actor_2)
4
+ (
5
+ actor_1.position.x < (actor_2.position.x + actor_2.width) &&
6
+ (actor_1.position.x + actor_1.width) > actor_2.position.x &&
7
+ actor_1.position.y < (actor_2.position.y + actor_2.height) &&
8
+ actor_1.position.y + actor_1.height > actor_2.position.y
9
+ )
10
+ end
11
+
12
+ def self.collision_at?(actor, x, y)
13
+ (
14
+ actor.position.x < x &&
15
+ (actor.position.x + actor.width) > x &&
16
+ actor.position.y < y &&
17
+ actor.position.y + actor.height > y
18
+ )
19
+ end
20
+
21
+ def self.draw_frame(x, y, width, height, stroke, color)
22
+ Gosu.draw_rect(x, y, width, stroke, color)
23
+ Gosu.draw_rect(x + (width - stroke), y, stroke, height, color)
24
+ Gosu.draw_rect(x, y + (height - stroke), width, stroke, color)
25
+ Gosu.draw_rect(x, y, stroke, height, color)
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fantasy
4
+ VERSION = "0.1.0"
5
+ end
data/lib/fantasy.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ require "gosu"
3
+
4
+ require_relative "fantasy/version"
5
+ require_relative "fantasy/draggable"
6
+ require_relative "fantasy/color"
7
+ require_relative "fantasy/actor"
8
+ require_relative "fantasy/coordinates"
9
+ require_relative "fantasy/utils"
10
+ require_relative "fantasy/global"
11
+ require_relative "fantasy/clock"
12
+ require_relative "fantasy/loop"
13
+ require_relative "fantasy/hud_text"
14
+ require_relative "fantasy/hud_image"
15
+ require_relative "fantasy/sound"
16
+ require_relative "fantasy/camera"
17
+ require_relative "fantasy/base"
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fantasy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Fernando Guillen
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-03-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gosu
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: vector2d
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.2.3
41
+ description: Simple toolbox library and lean API to build great mini games
42
+ email:
43
+ - fguillen.mail@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".rubocop.yml"
49
+ - CHANGELOG.md
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - bin/console
55
+ - bin/setup
56
+ - fantasy.gemspec
57
+ - fonts/OFL.txt
58
+ - fonts/VT323-Regular.ttf
59
+ - lib/fantasy.rb
60
+ - lib/fantasy/actor.rb
61
+ - lib/fantasy/base.rb
62
+ - lib/fantasy/camera.rb
63
+ - lib/fantasy/clock.rb
64
+ - lib/fantasy/color.rb
65
+ - lib/fantasy/coordinates.rb
66
+ - lib/fantasy/draggable.rb
67
+ - lib/fantasy/global.rb
68
+ - lib/fantasy/hud_image.rb
69
+ - lib/fantasy/hud_text.rb
70
+ - lib/fantasy/loop.rb
71
+ - lib/fantasy/sound.rb
72
+ - lib/fantasy/utils.rb
73
+ - lib/fantasy/version.rb
74
+ homepage: https://github.com/fguillen/fantasy
75
+ licenses:
76
+ - MIT
77
+ metadata:
78
+ allowed_push_host: https://rubygems.org
79
+ homepage_uri: https://github.com/fguillen/fantasy
80
+ source_code_uri: https://github.com/fguillen/fantasy
81
+ changelog_uri: https://github.com/fguillen/fantasy/blob/main/CHANGELOG.md
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 3.0.0
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.2.22
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Simple toolbox library and lean API to build great mini games
101
+ test_files: []