prkwars 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/prkwars +5 -0
- data/lib/prkwars/bullet.rb +30 -0
- data/lib/prkwars/bullet_turret.rb +20 -0
- data/lib/prkwars/enemy.rb +16 -0
- data/lib/prkwars/explosionparticle.rb +48 -0
- data/lib/prkwars/game.rb +16 -0
- data/lib/prkwars/gameover.rb +18 -0
- data/lib/prkwars/gamespace.rb +47 -0
- data/lib/prkwars/modules.rb +32 -0
- data/lib/prkwars/play.rb +195 -0
- data/lib/prkwars/player.rb +128 -0
- data/lib/prkwars/popuptext.rb +23 -0
- data/lib/prkwars/splash.rb +22 -0
- data/lib/prkwars/stalker.rb +39 -0
- data/lib/prkwars/turret.rb +54 -0
- data/lib/prkwars/warp.rb +18 -0
- data/lib/prkwars.rb +5 -0
- data/media/background.png +0 -0
- data/media/bullet.png +0 -0
- data/media/bullet_turret.png +0 -0
- data/media/gamespace.png +0 -0
- data/media/myparticle.png +0 -0
- data/media/player_ship.png +0 -0
- data/media/stalker.png +0 -0
- data/media/turrets.png +0 -0
- data/media/warp.png +0 -0
- metadata +71 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 1f148993f8f36cbef84d775439462a7ac06cc4c1
|
|
4
|
+
data.tar.gz: bd0bffc487588e44e113f51bd91536a257dd0af2
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8861ae042979ea10ef8bb8b6f5471ac7e55c328416e81091600566043095364b44bfa1f27ef1da6925c33ed47b8369e143d7267702e318866b03506ca5eedb76
|
|
7
|
+
data.tar.gz: 221db7ba52d96a1a4e4e11c8d035069fcca794b5fbc56fb078d8d727d35d36038dbf100f38c091e5657a089fd44e2a168671fdf0b3f153cb144b7679d50cdd9b
|
data/bin/prkwars
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative './modules.rb'
|
|
2
|
+
require 'chingu'
|
|
3
|
+
|
|
4
|
+
# Class representing a bullet shot by the player. Destroys itself
|
|
5
|
+
# if is present out of bounds.
|
|
6
|
+
class Bullet < Chingu::GameObject
|
|
7
|
+
trait :velocity
|
|
8
|
+
trait :bounding_box
|
|
9
|
+
trait :collision_detection
|
|
10
|
+
include GamespacePersistence
|
|
11
|
+
|
|
12
|
+
def initialize(gamespace, options = {})
|
|
13
|
+
super(options)
|
|
14
|
+
@image = Image['media/bullet.png']
|
|
15
|
+
@gamespace = gamespace
|
|
16
|
+
|
|
17
|
+
cache_bounding_box
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def update
|
|
21
|
+
return if in_bounds(self, @gamespace)
|
|
22
|
+
|
|
23
|
+
2.times do
|
|
24
|
+
ExplosionParticle.create(@gamespace,
|
|
25
|
+
x: @x, y: @y,
|
|
26
|
+
zorder: ZOrder::GAMEOBJECT)
|
|
27
|
+
destroy!
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require_relative './modules'
|
|
2
|
+
require 'chingu'
|
|
3
|
+
|
|
4
|
+
# A bullet shot by the Turret enemy unit. Destroys itself if out of bounds.
|
|
5
|
+
class BulletTurret < Chingu::GameObject
|
|
6
|
+
trait :velocity
|
|
7
|
+
trait :bounding_box
|
|
8
|
+
trait :collision_detection
|
|
9
|
+
include GamespacePersistence
|
|
10
|
+
|
|
11
|
+
def initialize(gamespace, options = {})
|
|
12
|
+
super(options)
|
|
13
|
+
@image = Image['media/bullet_turret.png']
|
|
14
|
+
@gamespace = gamespace
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update
|
|
18
|
+
destroy! unless in_bounds(self, @gamespace)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'chingu'
|
|
2
|
+
|
|
3
|
+
# A generic Enemy class inheriting from Chingu::Gameobject. Any enemy
|
|
4
|
+
# unit inherits from this class. The class contains a method
|
|
5
|
+
# which returns all the descendants - useful for checking all possible
|
|
6
|
+
# collisions.
|
|
7
|
+
class Enemy < Chingu::GameObject
|
|
8
|
+
attr_accessor :hp
|
|
9
|
+
trait :bounding_box
|
|
10
|
+
trait :collision_detection
|
|
11
|
+
trait :timer
|
|
12
|
+
|
|
13
|
+
def self.descendants
|
|
14
|
+
ObjectSpace.each_object(::Class).select { |klass| klass < self }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require_relative './modules'
|
|
2
|
+
require 'chingu'
|
|
3
|
+
|
|
4
|
+
# Class used for holding behaviour of particles during explosions of various
|
|
5
|
+
# entitites.
|
|
6
|
+
class ExplosionParticle < Chingu::GameObject
|
|
7
|
+
trait :velocity
|
|
8
|
+
trait :effect # fade
|
|
9
|
+
trait :bounding_box
|
|
10
|
+
include GamespacePersistence
|
|
11
|
+
|
|
12
|
+
def initialize(gamespace, options = {})
|
|
13
|
+
super(options)
|
|
14
|
+
|
|
15
|
+
@gamespace = gamespace
|
|
16
|
+
@fade_rate = -3
|
|
17
|
+
@mode = :default
|
|
18
|
+
@image = Image['media/myparticle.png']
|
|
19
|
+
@velocity_x = rand(-7..7)
|
|
20
|
+
@velocity_y = rand(-7..7)
|
|
21
|
+
@angle = Math.atan2(@velocity_x, @velocity_y) / Math::PI * 180 + 90
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def update
|
|
25
|
+
velocity_correct! unless bounding_box.collide_rect?(@gamespace.bounding_box)
|
|
26
|
+
destroy! if color.alpha.zero?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Method correcting the velocity if hitting a wall.
|
|
32
|
+
def velocity_correct!
|
|
33
|
+
if hitting_side
|
|
34
|
+
@velocity_x = -@velocity_x
|
|
35
|
+
else
|
|
36
|
+
@velocity_y = -@velocity_y
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def hitting_side
|
|
41
|
+
if bounding_box.left < @gamespace.bounding_box.left ||
|
|
42
|
+
bounding_box.right > @gamespace.bounding_box.right
|
|
43
|
+
true
|
|
44
|
+
else
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/prkwars/game.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require_relative './play'
|
|
2
|
+
require 'chingu'
|
|
3
|
+
include Gosu
|
|
4
|
+
|
|
5
|
+
# Game class which is reponsible for the whole game logic - generation
|
|
6
|
+
# of the game space, generation of enemies and checking collisions between
|
|
7
|
+
# them, destroying objects or reducing their HP if necessary.
|
|
8
|
+
class Game < Chingu::Window
|
|
9
|
+
attr_reader :player, :gamespace
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
super(1280, 720)
|
|
13
|
+
# push_game_state(Play)
|
|
14
|
+
push_game_state(Splash)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'chingu'
|
|
2
|
+
|
|
3
|
+
# Class representing the game over state shown when the player loses the game.
|
|
4
|
+
class GameOver < Chingu::GameState
|
|
5
|
+
def initialize(score, options = {})
|
|
6
|
+
super(options)
|
|
7
|
+
|
|
8
|
+
self.input = { space: :play, esc: :exit }
|
|
9
|
+
|
|
10
|
+
Chingu::Text.create("Final score: #{score}", x: 20, y: 20, size: 30)
|
|
11
|
+
Chingu::Text.create('Press <space> to play again, <esc> to exit.',
|
|
12
|
+
x: 20, y: 60)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def play
|
|
16
|
+
switch_game_state(Play.new)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'chingu'
|
|
2
|
+
|
|
3
|
+
# Class representing the space where the game is being played. Class is
|
|
4
|
+
# essentially an encapsulation for the big rectangle where the game can be
|
|
5
|
+
# played. Also holds possible spawn points for enemy entities.
|
|
6
|
+
class GameSpace < Chingu::GameObject
|
|
7
|
+
trait :collision_detection
|
|
8
|
+
trait :bounding_box
|
|
9
|
+
|
|
10
|
+
attr_accessor :player
|
|
11
|
+
attr_reader :spawn_points
|
|
12
|
+
|
|
13
|
+
def initialize(options = {})
|
|
14
|
+
super(options)
|
|
15
|
+
|
|
16
|
+
@image = Image['media/background.png']
|
|
17
|
+
setup_spawn_points
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def enemy_spawn_point(player_x, player_y)
|
|
21
|
+
sample_points = points_far_enough(player_x, player_y)
|
|
22
|
+
|
|
23
|
+
sample_points.sample
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def setup_spawn_points
|
|
29
|
+
@spawn_points = [[60, 60], [1200, 60], [60, 640], [1200, 640], [800, 640],
|
|
30
|
+
[800, 60], [800, 640], [250, 400], [500, 600],
|
|
31
|
+
[120, 640], [340, 520], [380, 640], [960, 80],
|
|
32
|
+
[1100, 420]]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def points_far_enough(player_x, player_y)
|
|
36
|
+
points = []
|
|
37
|
+
|
|
38
|
+
@spawn_points.each do |point|
|
|
39
|
+
dist = Math.sqrt((player_x - point[0]) * (player_x - point[0]) +
|
|
40
|
+
(player_y - point[1]) * (player_y - point[1]))
|
|
41
|
+
|
|
42
|
+
points.push(point) if dist > 130
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
points
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'chingu'
|
|
2
|
+
|
|
3
|
+
module ZOrder
|
|
4
|
+
GAMEOBJECT = 2
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module Constants
|
|
8
|
+
PLAYER_VELOCITY = 6
|
|
9
|
+
BULLET_VELOCITY = 12
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Module used by classes as a mixin for collision checking with the gamespace
|
|
13
|
+
# so that they do not get out of bounds.
|
|
14
|
+
module GamespacePersistence
|
|
15
|
+
def in_bounds(entity, gamespace)
|
|
16
|
+
return true if gamespace.bounding_box.contain?(entity.bounding_box)
|
|
17
|
+
false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def correct_coords(entity, gamespace)
|
|
21
|
+
if entity.bounding_box.top < gamespace.bounding_box.top # ceiling
|
|
22
|
+
entity.y += gamespace.bounding_box.top - entity.bounding_box.top
|
|
23
|
+
elsif entity.bounding_box.bottom > gamespace.bounding_box.bottom # floor
|
|
24
|
+
entity.y -= entity.bounding_box.bottom - gamespace.bounding_box.bottom
|
|
25
|
+
end
|
|
26
|
+
if entity.bounding_box.left < gamespace.bounding_box.left # left side
|
|
27
|
+
entity.x += gamespace.bounding_box.left - entity.bounding_box.left
|
|
28
|
+
elsif entity.bounding_box.right > gamespace.bounding_box.right # right side
|
|
29
|
+
entity.x -= entity.bounding_box.right - gamespace.bounding_box.right
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/prkwars/play.rb
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require 'chingu'
|
|
2
|
+
|
|
3
|
+
# Class holding the gamestate of the gameplay itself.
|
|
4
|
+
class Play < Chingu::GameState
|
|
5
|
+
trait :timer
|
|
6
|
+
|
|
7
|
+
attr_reader :player, :gamespace
|
|
8
|
+
|
|
9
|
+
PLAYER_START_X = 800
|
|
10
|
+
PLAYER_START_Y = 640
|
|
11
|
+
|
|
12
|
+
include GamespacePersistence
|
|
13
|
+
|
|
14
|
+
def initialize(options = {})
|
|
15
|
+
super(options)
|
|
16
|
+
|
|
17
|
+
self.input = { esc: :exit, p: :pause }
|
|
18
|
+
|
|
19
|
+
@gamespace = GameSpace.create(x: 1280 / 2, y: 720 / 2)
|
|
20
|
+
@player = Player.create(@gamespace,
|
|
21
|
+
zorder: ZOrder::GAMEOBJECT,
|
|
22
|
+
x: PLAYER_START_X, y: PLAYER_START_Y)
|
|
23
|
+
@gamespace.player = @player
|
|
24
|
+
|
|
25
|
+
setup_enemy_generation
|
|
26
|
+
setup_score_calculation
|
|
27
|
+
setup_display_messages
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def pause
|
|
31
|
+
switch_game_state(Splash)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def update
|
|
35
|
+
super
|
|
36
|
+
collide_enemies_bullets
|
|
37
|
+
collide_enemies_player
|
|
38
|
+
|
|
39
|
+
generate_random_enemy
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def draw
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def setup_score_calculation
|
|
49
|
+
@score = 0
|
|
50
|
+
@multiplier = 1
|
|
51
|
+
@score_this_life = 0
|
|
52
|
+
@next_multiplier = 2000
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def update_score
|
|
56
|
+
@score += 100 * @multiplier
|
|
57
|
+
@score_this_life += 100 * @multiplier
|
|
58
|
+
|
|
59
|
+
@message_score.text = "Score: #{@score}"
|
|
60
|
+
|
|
61
|
+
return if @score_this_life != @next_multiplier
|
|
62
|
+
|
|
63
|
+
@multiplier += 1
|
|
64
|
+
PopupText.create("#{@multiplier}x Multiplier", @player.x, @player.y - 10)
|
|
65
|
+
@next_multiplier *= 2
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def collide_enemies_bullets
|
|
69
|
+
Enemy.descendants.each do |enemy|
|
|
70
|
+
enemy.each_bounding_box_collision(Bullet) do |nmy, bullet|
|
|
71
|
+
10.times do
|
|
72
|
+
ExplosionParticle.create(@gamespace,
|
|
73
|
+
x: bullet.x, y: bullet.y,
|
|
74
|
+
zorder: ZOrder::GAMEOBJECT)
|
|
75
|
+
end
|
|
76
|
+
bullet.destroy!
|
|
77
|
+
nmy.take_damage
|
|
78
|
+
next unless nmy.hp.zero?
|
|
79
|
+
Warp.create(zorder: ZOrder::GAMEOBJECT, x: nmy.x, y: nmy.y)
|
|
80
|
+
PopupText.create((100 * @multiplier).to_s, nmy.x, nmy.y)
|
|
81
|
+
nmy.destroy
|
|
82
|
+
update_score
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def collide_enemies_player
|
|
88
|
+
Enemy.descendants.each do |enemy|
|
|
89
|
+
@player.each_bounding_box_collision(enemy) do
|
|
90
|
+
handle_player_death
|
|
91
|
+
return true # no need to check collisions of turret bullets
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
@player.each_bounding_box_collision(BulletTurret) do
|
|
96
|
+
handle_player_death
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def generate_random_enemy
|
|
101
|
+
@framecounter_turret += 1
|
|
102
|
+
generate_random_turret
|
|
103
|
+
|
|
104
|
+
@framecounter_stalker += 1
|
|
105
|
+
generate_random_stalker
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def generate_random_turret
|
|
109
|
+
return if @framecounter_turret != @nextgen_turret
|
|
110
|
+
|
|
111
|
+
x, y = generate_random_position
|
|
112
|
+
turret = Turret.create(@gamespace, zorder: ZOrder::GAMEOBJECT, x: x, y: y)
|
|
113
|
+
|
|
114
|
+
correct_coords(turret, @gamespace)
|
|
115
|
+
|
|
116
|
+
create_warp(x, y)
|
|
117
|
+
|
|
118
|
+
@framecounter_turret = 0
|
|
119
|
+
@nextgen_turret = rand(80..150)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def generate_random_stalker
|
|
123
|
+
return if @framecounter_stalker != @nextgen_stalker
|
|
124
|
+
|
|
125
|
+
x, y = generate_random_position
|
|
126
|
+
Stalker.create(@gamespace, zorder: ZOrder::GAMEOBJECT, x: x, y: y)
|
|
127
|
+
create_warp(x, y)
|
|
128
|
+
|
|
129
|
+
@framecounter_stalker = 0
|
|
130
|
+
@nextgen_stalker = rand(20..50)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def create_warp(x, y)
|
|
134
|
+
Warp.create(zorder: ZOrder::GAMEOBJECT, x: x, y: y)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def generate_random_position
|
|
138
|
+
x, y = @gamespace.enemy_spawn_point(@player.x, @player.y)
|
|
139
|
+
|
|
140
|
+
x += rand(-30..30)
|
|
141
|
+
y += rand(-30..30)
|
|
142
|
+
|
|
143
|
+
[x, y]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def handle_game_over
|
|
147
|
+
switch_game_state(GameOver.new(@score))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def handle_player_death
|
|
151
|
+
kill_everything
|
|
152
|
+
@player.lives -= 1
|
|
153
|
+
handle_game_over if @player.lives.zero?
|
|
154
|
+
|
|
155
|
+
500.times do
|
|
156
|
+
ExplosionParticle.create(@gamespace,
|
|
157
|
+
x: @player.x, y: @player.y,
|
|
158
|
+
zorder: ZOrder::GAMEOBJECT)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
setup_enemy_generation
|
|
162
|
+
@message_lives.text = "Lives: #{@player.lives}"
|
|
163
|
+
@score_this_life = 0
|
|
164
|
+
@next_multiplier = 2000
|
|
165
|
+
@multiplier = 1
|
|
166
|
+
PopupText.create("#{@multiplier}x Multiplier", @player.x, @player.y - 10)
|
|
167
|
+
@player.x = PLAYER_START_X
|
|
168
|
+
@player.y = PLAYER_START_Y
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def kill_everything
|
|
172
|
+
Enemy.descendants.each do |enemy|
|
|
173
|
+
ObjectSpace.each_object(enemy, &:destroy!)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
ObjectSpace.each_object(BulletTurret, &:destroy!)
|
|
177
|
+
|
|
178
|
+
ObjectSpace.each_object(Bullet, &:destroy!)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def setup_enemy_generation
|
|
182
|
+
@framecounter_turret = 0
|
|
183
|
+
@nextgen_turret = rand(150..300)
|
|
184
|
+
@framecounter_stalker = 0
|
|
185
|
+
@nextgen_stalker = rand(110..180)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def setup_display_messages
|
|
189
|
+
@message_lives = Chingu::Text.create("Lives: #{@player.lives}",
|
|
190
|
+
x: 1020, y: 5, size: 30)
|
|
191
|
+
|
|
192
|
+
@message_score = Chingu::Text.create("Score: #{@score}",
|
|
193
|
+
x: 100, y: 5, size: 30)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
require_relative './modules'
|
|
2
|
+
require 'chingu'
|
|
3
|
+
|
|
4
|
+
# Class holding all the logic related to the player - input controller
|
|
5
|
+
# and any manipulation of player's sprite.
|
|
6
|
+
class Player < Chingu::GameObject
|
|
7
|
+
trait :bounding_box
|
|
8
|
+
trait :collision_detection
|
|
9
|
+
|
|
10
|
+
attr_accessor :lives
|
|
11
|
+
attr_reader :invulnerable
|
|
12
|
+
|
|
13
|
+
include GamespacePersistence
|
|
14
|
+
STARTING_LIVES = 3
|
|
15
|
+
SHOOT_GAP = 8
|
|
16
|
+
STARTING_BOMBS = 3
|
|
17
|
+
INVUL_FRAMES = 30
|
|
18
|
+
|
|
19
|
+
def initialize(gamespace, options = {})
|
|
20
|
+
super(options)
|
|
21
|
+
|
|
22
|
+
@image = Image['./media/player_ship.png']
|
|
23
|
+
self.input = { holding_w: :move_up, holding_a: :move_left,
|
|
24
|
+
holding_d: :move_right, holding_s: :move_down,
|
|
25
|
+
holding_left: :shoot_left, holding_right: :shoot_right,
|
|
26
|
+
holding_up: :shoot_up, holding_down: :shoot_down }
|
|
27
|
+
init_vector_variables
|
|
28
|
+
|
|
29
|
+
@gamespace = gamespace
|
|
30
|
+
@lives = STARTING_LIVES
|
|
31
|
+
@bombs = STARTING_BOMBS
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def move_up
|
|
35
|
+
@y -= Constants::PLAYER_VELOCITY
|
|
36
|
+
@y_change = -Constants::PLAYER_VELOCITY
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def move_down
|
|
40
|
+
@y += Constants::PLAYER_VELOCITY
|
|
41
|
+
@y_change = Constants::PLAYER_VELOCITY
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def move_left
|
|
45
|
+
@x -= Constants::PLAYER_VELOCITY
|
|
46
|
+
@x_change = -Constants::PLAYER_VELOCITY
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def move_right
|
|
50
|
+
@x += Constants::PLAYER_VELOCITY
|
|
51
|
+
@x_change = Constants::PLAYER_VELOCITY
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def shoot_left
|
|
55
|
+
@shoot_vec_x = -Constants::BULLET_VELOCITY
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def shoot_right
|
|
59
|
+
@shoot_vec_x = Constants::BULLET_VELOCITY
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def shoot_up
|
|
63
|
+
@shoot_vec_y = -Constants::BULLET_VELOCITY
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def shoot_down
|
|
67
|
+
@shoot_vec_y = Constants::BULLET_VELOCITY
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def update
|
|
71
|
+
rotate_player
|
|
72
|
+
correct_coords(self, @gamespace) unless in_bounds(self, @gamespace)
|
|
73
|
+
@shoot_timer += 1
|
|
74
|
+
return unless @shoot_timer == SHOOT_GAP
|
|
75
|
+
|
|
76
|
+
shoot_bullet
|
|
77
|
+
@shoot_timer = 0
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def init_vector_variables
|
|
83
|
+
@x_change = 0 # Used for rotating the triangular sprite
|
|
84
|
+
@y_change = 0
|
|
85
|
+
@shoot_timer = 0
|
|
86
|
+
@shoot_vec_x = 0 # Used for deciding the shooting direction
|
|
87
|
+
@shoot_vec_y = 0
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def rotate_player
|
|
91
|
+
return unless @x_change != 0 || @y_change != 0
|
|
92
|
+
|
|
93
|
+
rot_to = Math.atan2(@y_change, @x_change) / Math::PI * 180 + 90
|
|
94
|
+
|
|
95
|
+
self.angle = rot_to
|
|
96
|
+
|
|
97
|
+
@x_change = 0
|
|
98
|
+
@y_change = 0
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def shoot_bullet
|
|
102
|
+
return unless @shoot_vec_x != 0 || @shoot_vec_y != 0
|
|
103
|
+
|
|
104
|
+
bullet_angle = Math.atan2(@shoot_vec_y, @shoot_vec_x) / Math::PI * 180 - 180
|
|
105
|
+
|
|
106
|
+
norm_x = -@shoot_vec_y
|
|
107
|
+
norm_y = @shoot_vec_x
|
|
108
|
+
|
|
109
|
+
dist_norm = Math.sqrt(norm_x * norm_x + norm_y * norm_y)
|
|
110
|
+
|
|
111
|
+
unit_x = norm_x / dist_norm
|
|
112
|
+
unit_y = norm_y / dist_norm
|
|
113
|
+
|
|
114
|
+
Bullet.create(@gamespace,
|
|
115
|
+
zorder: ZOrder::GAMEOBJECT, x: @x + unit_x * 7,
|
|
116
|
+
y: @y + unit_y * 7,
|
|
117
|
+
velocity_x: @shoot_vec_x, velocity_y: @shoot_vec_y,
|
|
118
|
+
angle: bullet_angle)
|
|
119
|
+
|
|
120
|
+
Bullet.create(@gamespace,
|
|
121
|
+
zorder: ZOrder::GAMEOBJECT, x: @x - unit_x * 7,
|
|
122
|
+
y: @y - unit_y * 7,
|
|
123
|
+
velocity_x: @shoot_vec_x, velocity_y: @shoot_vec_y,
|
|
124
|
+
angle: bullet_angle)
|
|
125
|
+
@shoot_vec_x = 0
|
|
126
|
+
@shoot_vec_y = 0
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'chingu'
|
|
2
|
+
include Gosu
|
|
3
|
+
|
|
4
|
+
# Class used for messages that pop up on the screen whenever something
|
|
5
|
+
# relevant happens - e.g. score goes up.
|
|
6
|
+
class PopupText < Chingu::GameObject
|
|
7
|
+
trait :effect # fade
|
|
8
|
+
trait :velocity # move up slowly
|
|
9
|
+
|
|
10
|
+
def initialize(message, x, y, options = {})
|
|
11
|
+
super(options)
|
|
12
|
+
|
|
13
|
+
@velocity_x = 0
|
|
14
|
+
@velocity_y = -2
|
|
15
|
+
@msg = Chingu::Text.create(message, x: x, y: y, size: 30)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def update
|
|
19
|
+
@msg.y -= 1
|
|
20
|
+
@msg.color.alpha -= 4
|
|
21
|
+
@msg.destroy! if color.alpha.zero?
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'chingu'
|
|
2
|
+
|
|
3
|
+
# Class representing the game state right after turning the game on.
|
|
4
|
+
class Splash < Chingu::GameState
|
|
5
|
+
def setup
|
|
6
|
+
self.input = { space: :play, esc: :exit }
|
|
7
|
+
Chingu::Text.create('PRK WARS',
|
|
8
|
+
x: 20, y: 20, size: 60, color: Gosu::Color::RED)
|
|
9
|
+
Chingu::Text.create('A simple Grid Wars/Geometry Wars clone',
|
|
10
|
+
x: 20, y: 90, size: 30)
|
|
11
|
+
Chingu::Text.create('Press <space> to play the game, <esc> to exit.',
|
|
12
|
+
x: 20, y: 130, size: 30)
|
|
13
|
+
Chingu::Text.create('Controls: WASD: movement, arrow keys: shooting'\
|
|
14
|
+
' direction.',
|
|
15
|
+
x: 20, y: 170, size: 30)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def play
|
|
19
|
+
transitional_game_state(Chingu::GameStates::FadeTo, speed: 10)
|
|
20
|
+
switch_game_state(Play.new)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require_relative './enemy'
|
|
2
|
+
require 'chingu'
|
|
3
|
+
|
|
4
|
+
# Class representing the Stalker enemy unit which is mobile but unable to
|
|
5
|
+
# shoot and endlessly moves towards the player unit.
|
|
6
|
+
class Stalker < Enemy
|
|
7
|
+
trait :velocity
|
|
8
|
+
include GamespacePersistence
|
|
9
|
+
VELOCITY = 2
|
|
10
|
+
HP = 1
|
|
11
|
+
|
|
12
|
+
def initialize(gamespace, options = {})
|
|
13
|
+
super(options)
|
|
14
|
+
|
|
15
|
+
@image = Image['./media/stalker.png']
|
|
16
|
+
|
|
17
|
+
@gamespace = gamespace
|
|
18
|
+
@hp = HP
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def update
|
|
22
|
+
dist_x = @gamespace.player.x - @x
|
|
23
|
+
dist_y = @gamespace.player.y - @y
|
|
24
|
+
|
|
25
|
+
rot_to = Math.atan2(dist_y, dist_x) / Math::PI * 180 + 90
|
|
26
|
+
|
|
27
|
+
dist = Math.sqrt(dist_x * dist_x + dist_y * dist_y)
|
|
28
|
+
|
|
29
|
+
ticks = dist / VELOCITY
|
|
30
|
+
|
|
31
|
+
@velocity_x = dist_x / ticks
|
|
32
|
+
@velocity_y = dist_y / ticks
|
|
33
|
+
@angle = rot_to
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def take_damage
|
|
37
|
+
@hp -= 1
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require_relative './modules'
|
|
2
|
+
require_relative './enemy'
|
|
3
|
+
require_relative './bullet_turret'
|
|
4
|
+
require 'chingu'
|
|
5
|
+
|
|
6
|
+
# Class representing the Turret enemy unit which is stationary and endlessly
|
|
7
|
+
# shoots bullets in the direction of the player.
|
|
8
|
+
class Turret < Enemy
|
|
9
|
+
SHOOT_GAP = 40
|
|
10
|
+
VELOCITY = 3
|
|
11
|
+
HP = 3
|
|
12
|
+
|
|
13
|
+
def initialize(gamespace, options = {})
|
|
14
|
+
super(options)
|
|
15
|
+
|
|
16
|
+
@animation = Chingu::Animation.new(file: 'media/turrets.png', size: 32)
|
|
17
|
+
|
|
18
|
+
@gamespace = gamespace
|
|
19
|
+
@hp = HP
|
|
20
|
+
@shoot_timer = 0
|
|
21
|
+
@image = @animation[@hp - 1]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def update
|
|
25
|
+
@shoot_timer += 1
|
|
26
|
+
return unless @shoot_timer == SHOOT_GAP
|
|
27
|
+
|
|
28
|
+
shoot_bullet
|
|
29
|
+
@shoot_timer = 0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def take_damage
|
|
33
|
+
@hp -= 1
|
|
34
|
+
@image = @animation[@hp - 1]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def shoot_bullet
|
|
40
|
+
dist_x = @gamespace.player.x - @x
|
|
41
|
+
dist_y = @gamespace.player.y - @y
|
|
42
|
+
|
|
43
|
+
bullet_angle = Math.atan2(dist_y, dist_x) / Math::PI * 180 + 90
|
|
44
|
+
|
|
45
|
+
dist = Math.sqrt(dist_x * dist_x + dist_y * dist_y)
|
|
46
|
+
|
|
47
|
+
ticks = dist / VELOCITY
|
|
48
|
+
|
|
49
|
+
BulletTurret.create(@gamespace,
|
|
50
|
+
zorder: ZOrder::GAMEOBJECT, x: @x,
|
|
51
|
+
y: @y, velocity_x: dist_x / ticks,
|
|
52
|
+
velocity_y: dist_y / ticks, angle: bullet_angle)
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/prkwars/warp.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'chingu'
|
|
2
|
+
|
|
3
|
+
# Class holding the warp effect used whenever an enemy game object
|
|
4
|
+
# spawns somewhere.
|
|
5
|
+
class Warp < Chingu::GameObject
|
|
6
|
+
trait :effect # fade
|
|
7
|
+
|
|
8
|
+
def initialize(options = {})
|
|
9
|
+
super(options)
|
|
10
|
+
|
|
11
|
+
@fade_rate = -8
|
|
12
|
+
@image = Image['media/warp.png']
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def update
|
|
16
|
+
destroy! if color.alpha.zero?
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/prkwars.rb
ADDED
|
Binary file
|
data/media/bullet.png
ADDED
|
Binary file
|
|
Binary file
|
data/media/gamespace.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/media/stalker.png
ADDED
|
Binary file
|
data/media/turrets.png
ADDED
|
Binary file
|
data/media/warp.png
ADDED
|
Binary file
|
metadata
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: prkwars
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Marek Pikna
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-02-16 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A simple Grid Wars clone made as a project for the Ruby class taught
|
|
14
|
+
at FIT CTU.
|
|
15
|
+
email: mar.pikna@gmail.com
|
|
16
|
+
executables:
|
|
17
|
+
- prkwars
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- bin/prkwars
|
|
22
|
+
- lib/prkwars.rb
|
|
23
|
+
- lib/prkwars/bullet.rb
|
|
24
|
+
- lib/prkwars/bullet_turret.rb
|
|
25
|
+
- lib/prkwars/enemy.rb
|
|
26
|
+
- lib/prkwars/explosionparticle.rb
|
|
27
|
+
- lib/prkwars/game.rb
|
|
28
|
+
- lib/prkwars/gameover.rb
|
|
29
|
+
- lib/prkwars/gamespace.rb
|
|
30
|
+
- lib/prkwars/modules.rb
|
|
31
|
+
- lib/prkwars/play.rb
|
|
32
|
+
- lib/prkwars/player.rb
|
|
33
|
+
- lib/prkwars/popuptext.rb
|
|
34
|
+
- lib/prkwars/splash.rb
|
|
35
|
+
- lib/prkwars/stalker.rb
|
|
36
|
+
- lib/prkwars/turret.rb
|
|
37
|
+
- lib/prkwars/warp.rb
|
|
38
|
+
- media/background.png
|
|
39
|
+
- media/bullet.png
|
|
40
|
+
- media/bullet_turret.png
|
|
41
|
+
- media/gamespace.png
|
|
42
|
+
- media/myparticle.png
|
|
43
|
+
- media/player_ship.png
|
|
44
|
+
- media/stalker.png
|
|
45
|
+
- media/turrets.png
|
|
46
|
+
- media/warp.png
|
|
47
|
+
homepage: http://rubygems.org/gems/prkwars
|
|
48
|
+
licenses:
|
|
49
|
+
- MIT
|
|
50
|
+
metadata: {}
|
|
51
|
+
post_install_message:
|
|
52
|
+
rdoc_options: []
|
|
53
|
+
require_paths:
|
|
54
|
+
- lib
|
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '0'
|
|
65
|
+
requirements: []
|
|
66
|
+
rubyforge_project:
|
|
67
|
+
rubygems_version: 2.6.13
|
|
68
|
+
signing_key:
|
|
69
|
+
specification_version: 4
|
|
70
|
+
summary: Prk wars - a Grid Wars clone
|
|
71
|
+
test_files: []
|