chichilku3 14.0.4 → 15.0.0
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 +4 -4
- data/bin/chichilku3 +1 -0
- data/bin/chichilku3-server +1 -0
- data/lib/client/chichilku3.rb +3 -1
- data/lib/client/client.rb +112 -59
- data/lib/client/client_cfg.rb +2 -1
- data/lib/client/gui.rb +219 -197
- data/lib/client/img/stick128/noarms/stick0.png +0 -0
- data/lib/client/img/stick128/noarms/stick1.png +0 -0
- data/lib/client/img/stick128/noarms/stick2.png +0 -0
- data/lib/client/img/stick128/noarms/stick3.png +0 -0
- data/lib/client/img/stick128/noarms/stick4.png +0 -0
- data/lib/client/img/stick128/noarms/stick5.png +0 -0
- data/lib/client/keys.rb +29 -0
- data/lib/client/particles.rb +54 -0
- data/lib/client/scoreboard.rb +29 -27
- data/lib/server/chichilku3_server.rb +169 -64
- data/lib/server/gamelogic.rb +107 -51
- data/lib/server/server_cfg.rb +2 -0
- data/lib/share/config.rb +21 -7
- data/lib/share/console.rb +22 -5
- data/lib/share/game_map.rb +279 -0
- data/lib/share/math.rb +14 -0
- data/lib/share/network.rb +47 -42
- data/lib/share/player.rb +168 -105
- data/lib/share/projectile.rb +97 -98
- data/lib/share/string.rb +24 -0
- data/server.json +3 -2
- metadata +42 -22
- data/lib/client/img/battle1024x576.png +0 -0
- data/lib/client/img/grass1024x512.png +0 -0
- data/lib/client/img/stick128/stick_noarms.png +0 -0
- data/lib/client/test.rb +0 -39
- data/lib/client/text.rb +0 -86
data/lib/server/gamelogic.rb
CHANGED
@@ -1,26 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../share/math'
|
4
|
+
|
5
|
+
# high level game logic
|
1
6
|
class GameLogic
|
2
7
|
def initialize(console)
|
3
8
|
@console = console
|
4
9
|
@alive_players = 0
|
5
10
|
end
|
6
11
|
|
12
|
+
def on_player_connect(client, players)
|
13
|
+
player = Player.get_player_by_id(players, client[PLAYER_ID])
|
14
|
+
return if player.nil?
|
15
|
+
|
16
|
+
port, ip = Socket.unpack_sockaddr_in(client[NET_CLIENT].getpeername)
|
17
|
+
@console.log "player joined ID=#{player.id} IP=#{ip}:#{port} name='#{player.name}'"
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_player_disconnect(client, players)
|
21
|
+
player = Player.get_player_by_id(players, client[PLAYER_ID])
|
22
|
+
return if player.nil?
|
23
|
+
|
24
|
+
@console.log "player left ID=#{player.id} name='#{player.name}'"
|
25
|
+
end
|
26
|
+
|
7
27
|
def check_collide(players, player)
|
8
28
|
players.each do |other|
|
9
29
|
next if other == player
|
10
|
-
next
|
30
|
+
next unless player.collide[:down]
|
11
31
|
|
12
32
|
x_force = player.check_player_collide(other)
|
13
|
-
player.apply_force(x_force, -8)
|
33
|
+
player.apply_force(x_force, -8) unless x_force.zero?
|
14
34
|
end
|
15
35
|
end
|
16
36
|
|
17
|
-
def tick(players, dt)
|
37
|
+
def tick(game_map, players, dt, tick)
|
18
38
|
players.each do |player|
|
19
39
|
# reset values (should stay first)
|
20
40
|
player.reset_collide
|
21
41
|
|
22
|
-
gravity(player, dt)
|
42
|
+
gravity(game_map, player, dt, tick)
|
23
43
|
player.tick
|
44
|
+
game_map_collision_vertical(game_map, player)
|
45
|
+
if player.dx.positive?
|
46
|
+
player.check_move_right(game_map)
|
47
|
+
elsif player.dx.negative?
|
48
|
+
player.check_move_left(game_map)
|
49
|
+
end
|
24
50
|
player.projectile.tick(players)
|
25
51
|
# player collsions works
|
26
52
|
# but it eats performance and delays jumping
|
@@ -28,14 +54,51 @@ class GameLogic
|
|
28
54
|
end
|
29
55
|
end
|
30
56
|
|
31
|
-
def
|
57
|
+
def game_map_collision_vertical(game_map, player)
|
58
|
+
if player.dy.positive?
|
59
|
+
# left bottom
|
60
|
+
col = game_map.collision?(player.x / TILE_SIZE, (player.y + player.h) / TILE_SIZE)
|
61
|
+
if col
|
62
|
+
player.y = col[:y] * TILE_SIZE
|
63
|
+
player.y -= player.crouching? ? PLAYER_SIZE / 2 : PLAYER_SIZE
|
64
|
+
player.do_collide(:down, true)
|
65
|
+
end
|
66
|
+
# right bottom
|
67
|
+
col = game_map.collision?((player.x + player.w) / TILE_SIZE, (player.y + player.h) / TILE_SIZE)
|
68
|
+
if col
|
69
|
+
player.y = col[:y] * TILE_SIZE
|
70
|
+
player.y -= player.crouching? ? PLAYER_SIZE / 2 : PLAYER_SIZE
|
71
|
+
player.do_collide(:down, true)
|
72
|
+
end
|
73
|
+
elsif player.dy.negative?
|
74
|
+
# left top
|
75
|
+
col = game_map.collision?(player.x / TILE_SIZE, player.y / TILE_SIZE)
|
76
|
+
if col
|
77
|
+
player.y = (col[:y] * TILE_SIZE) + player.h
|
78
|
+
player.y += 1
|
79
|
+
player.y += PLAYER_SIZE / 2 if player.crouching?
|
80
|
+
player.do_collide(:up, true)
|
81
|
+
end
|
82
|
+
# right top
|
83
|
+
col = game_map.collision?((player.x + player.w) / TILE_SIZE, player.y / TILE_SIZE)
|
84
|
+
if col
|
85
|
+
player.y = (col[:y] * TILE_SIZE) + player.h
|
86
|
+
player.y += 1
|
87
|
+
player.y += PLAYER_SIZE / 2 if player.crouching?
|
88
|
+
player.do_collide(:up, true)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def handle_client_requests(game_map, data, id, players, _dt)
|
32
95
|
player = Player.get_player_by_id(players, id)
|
33
96
|
if player.nil?
|
34
97
|
@console.log "WARNING failed to update nil player with id=#{id}"
|
35
|
-
if players.count
|
36
|
-
@console.log
|
98
|
+
if players.count.positive?
|
99
|
+
@console.log 'connected players:'
|
37
100
|
else
|
38
|
-
@console.log
|
101
|
+
@console.log 'no players currently connected!'
|
39
102
|
end
|
40
103
|
players.each do |p|
|
41
104
|
@console.log "id=#{p.id} name='#{p.name}'"
|
@@ -44,28 +107,37 @@ class GameLogic
|
|
44
107
|
end
|
45
108
|
|
46
109
|
# reset values (should stay first)
|
47
|
-
player.
|
110
|
+
player.wants_crouch = false
|
48
111
|
|
49
112
|
# move request
|
50
113
|
if data[0] == '1'
|
51
114
|
@console.dbg "player=#{id} wants to crouch"
|
52
|
-
player.
|
53
|
-
player.
|
115
|
+
player.crouch!
|
116
|
+
player.wants_crouch = true
|
117
|
+
player.x -= PLAYER_SIZE / 4 unless player.was_crouching
|
118
|
+
# TODO: why is it checking right when on left side!?
|
119
|
+
if closest_interval_side(TILE_SIZE, player.x) == SIDE_LEFT
|
120
|
+
player.check_move_right(game_map)
|
121
|
+
else
|
122
|
+
player.check_move_left(game_map)
|
123
|
+
end
|
54
124
|
player.was_crouching = true
|
55
125
|
end
|
56
126
|
if data[1] == 'l'
|
127
|
+
game_map_collision_vertical(game_map, player)
|
57
128
|
@console.dbg "player=#{id} wants to walk left"
|
58
|
-
player.move_left
|
129
|
+
player.move_left(game_map)
|
59
130
|
end
|
60
131
|
if data[1] == 'r'
|
61
132
|
@console.dbg "player=#{id} wants to walk right"
|
62
|
-
player
|
133
|
+
game_map_collision_vertical(game_map, player)
|
134
|
+
player.move_right(game_map)
|
63
135
|
end
|
64
136
|
if data[2] == '1'
|
65
137
|
@console.dbg "player=#{id} wants to jump"
|
66
138
|
player.do_jump
|
67
139
|
end
|
68
|
-
if data[3] == '1' && player.
|
140
|
+
if data[3] == '1' && player.crouching? == false
|
69
141
|
@console.dbg "player=#{id} wants to fire"
|
70
142
|
player.fire_ticks += 1
|
71
143
|
if player.fire_ticks > 29
|
@@ -76,39 +148,40 @@ class GameLogic
|
|
76
148
|
player.state[:fire] = 1
|
77
149
|
end
|
78
150
|
else
|
79
|
-
if player.fire_ticks
|
80
|
-
dx = (player.
|
81
|
-
dy = (player.
|
151
|
+
if player.fire_ticks.positive?
|
152
|
+
dx = (player.aim_x - player.x).clamp(-200, 200) / 20
|
153
|
+
dy = (player.aim_y - player.y).clamp(-200, 200) / 20
|
82
154
|
dx *= (player.fire_ticks / 10).clamp(1, 3)
|
83
155
|
dy *= (player.fire_ticks / 10).clamp(1, 3)
|
84
|
-
player.projectile.fire(player.x + TILE_SIZE/4, player.y + TILE_SIZE/2, dx, dy, player)
|
156
|
+
player.projectile.fire(player.x + TILE_SIZE / 4, player.y + TILE_SIZE / 2, dx, dy, player)
|
85
157
|
end
|
86
158
|
player.fire_ticks = 0
|
87
159
|
player.state[:fire] = 0
|
88
160
|
end
|
89
|
-
player.
|
90
|
-
player.
|
91
|
-
# player.projectile.x = player.
|
92
|
-
# player.projectile.y = player.
|
161
|
+
player.aim_x = net_unpack_bigint(data[4..5])
|
162
|
+
player.aim_y = net_unpack_bigint(data[6..7])
|
163
|
+
# player.projectile.x = player.aim_x + 20
|
164
|
+
# player.projectile.y = player.aim_y + 20
|
93
165
|
|
94
|
-
player.
|
166
|
+
player.check_out_of_game_map
|
95
167
|
|
96
168
|
# return updated players
|
97
169
|
players
|
98
170
|
end
|
99
171
|
|
100
|
-
def posttick(players,
|
172
|
+
def posttick(game_map, players, _dt)
|
101
173
|
players.each do |player|
|
102
174
|
# stopped crouching -> stand up
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
175
|
+
next unless player.was_crouching && player.wants_crouch == false
|
176
|
+
|
177
|
+
player.x += PLAYER_SIZE / 4
|
178
|
+
player.was_crouching = false
|
179
|
+
player.stop_crouch!
|
180
|
+
game_map_collision_vertical(game_map, player)
|
108
181
|
end
|
109
182
|
end
|
110
183
|
|
111
|
-
def gravity(player,
|
184
|
+
def gravity(game_map, player, _dt, _tick)
|
112
185
|
if player.dead
|
113
186
|
player.dead_ticks += 1
|
114
187
|
player.state[:bleeding] = true
|
@@ -117,31 +190,14 @@ class GameLogic
|
|
117
190
|
player.state[:bleeding] = false
|
118
191
|
player.die
|
119
192
|
end
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
player.dead_ticks = 0
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# outside of the save zone
|
128
|
-
if player.x < 214 || player.x > 800 || player.dead
|
129
|
-
if player.y + player.h > 484
|
130
|
-
# player.collide[:down] = true
|
131
|
-
player.do_collide(:down, true)
|
132
|
-
return
|
133
|
-
end
|
134
|
-
else # on the save zone
|
135
|
-
if player.y + player.h > 324
|
136
|
-
# player.collide[:down] = true
|
137
|
-
player.do_collide(:down, true)
|
138
|
-
return
|
139
|
-
end
|
193
|
+
elsif game_map.death?(player.x / TILE_SIZE, (player.y + player.h) / TILE_SIZE)
|
194
|
+
player.dead = true
|
195
|
+
player.dead_ticks = 0
|
140
196
|
end
|
141
197
|
|
142
198
|
# grav = 100000 * dt
|
143
199
|
# @console.log "grav: #{grav}"
|
144
200
|
# player.y += grav
|
145
|
-
player.dy +=
|
201
|
+
player.dy += 1 if player.dy < 16
|
146
202
|
end
|
147
203
|
end
|
data/lib/server/server_cfg.rb
CHANGED
data/lib/share/config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'os'
|
3
5
|
require 'fileutils'
|
@@ -7,7 +9,7 @@ class Config
|
|
7
9
|
attr_reader :data, :chichilku3_dir
|
8
10
|
|
9
11
|
def initialize(console, file)
|
10
|
-
@chichilku3_dir =
|
12
|
+
@chichilku3_dir = ''
|
11
13
|
if OS.linux?
|
12
14
|
@chichilku3_dir = "#{ENV['HOME']}/.chichilku/chichilku3/"
|
13
15
|
elsif OS.mac?
|
@@ -15,13 +17,24 @@ class Config
|
|
15
17
|
# elsif OS.windows?
|
16
18
|
# @chichilku3_dir = "%APPDATA%\\chichilku\\chichilku3\\"
|
17
19
|
else
|
18
|
-
puts
|
20
|
+
puts 'os not supported.'
|
19
21
|
exit
|
20
22
|
end
|
21
|
-
puts "path:
|
23
|
+
puts "path: #{@chichilku3_dir}"
|
22
24
|
FileUtils.mkdir_p @chichilku3_dir
|
23
25
|
FileUtils.mkdir_p "#{@chichilku3_dir}recordings"
|
26
|
+
FileUtils.mkdir_p "#{@chichilku3_dir}maps_b64"
|
27
|
+
FileUtils.mkdir_p "#{@chichilku3_dir}downloadedmaps"
|
28
|
+
FileUtils.mkdir_p "#{@chichilku3_dir}tmp"
|
29
|
+
unless File.directory? "#{@chichilku3_dir}maps"
|
30
|
+
if File.directory? 'maps'
|
31
|
+
FileUtils.cp_r 'maps', "#{@chichilku3_dir}maps"
|
32
|
+
else
|
33
|
+
FileUtils.mkdir_p "#{@chichilku3_dir}maps"
|
34
|
+
end
|
35
|
+
end
|
24
36
|
create_default_cfg(file, "#{@chichilku3_dir}/#{file}")
|
37
|
+
@source_file = file
|
25
38
|
@file = @chichilku3_dir + file
|
26
39
|
@console = console
|
27
40
|
@data = load
|
@@ -31,7 +44,7 @@ class Config
|
|
31
44
|
return if File.file?(to)
|
32
45
|
|
33
46
|
tmp = JSON.parse(File.read(from))
|
34
|
-
File.open(to,
|
47
|
+
File.open(to, 'w') do |f|
|
35
48
|
f.write(tmp.to_json)
|
36
49
|
end
|
37
50
|
end
|
@@ -41,13 +54,14 @@ class Config
|
|
41
54
|
end
|
42
55
|
|
43
56
|
def load
|
57
|
+
defaults = JSON.parse(File.read(@source_file))
|
44
58
|
data = JSON.parse(File.read(@file))
|
45
|
-
data =
|
46
|
-
data
|
59
|
+
data = defaults.merge(data)
|
60
|
+
sanitize_data(data)
|
47
61
|
end
|
48
62
|
|
49
63
|
def save
|
50
|
-
File.open(@file,
|
64
|
+
File.open(@file, 'w') do |f|
|
51
65
|
f.write(JSON.pretty_generate(data))
|
52
66
|
end
|
53
67
|
end
|
data/lib/share/console.rb
CHANGED
@@ -1,22 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'string'
|
1
4
|
DEBUG = false
|
5
|
+
DEBUG_PHYSICS = true
|
2
6
|
|
3
7
|
# Console used by Client and Server
|
4
8
|
class Console
|
5
9
|
def log(message)
|
6
|
-
|
7
|
-
puts format("[%02d:%02d:%02d][log] %s", t.hour, t.min, t.sec, message)
|
10
|
+
log_type('log', message)
|
8
11
|
end
|
9
12
|
|
10
13
|
def err(message)
|
11
|
-
|
12
|
-
|
14
|
+
log_type('error'.red, message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def wrn(message)
|
18
|
+
log_type('warning'.yellow, message)
|
13
19
|
end
|
14
20
|
|
15
21
|
def dbg(message)
|
16
22
|
return unless DEBUG
|
17
23
|
|
24
|
+
log_type('debug'.pink, message)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def log_type(type, message)
|
18
30
|
t = Time.now
|
19
|
-
puts format(
|
31
|
+
puts format('[%<hour>02d:%<min>02d:%<sec>02d][%<type>s] %<msg>s',
|
32
|
+
hour: t.hour,
|
33
|
+
min: t.min,
|
34
|
+
sec: t.sec,
|
35
|
+
type: type,
|
36
|
+
msg: message)
|
20
37
|
end
|
21
38
|
end
|
22
39
|
|
@@ -0,0 +1,279 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'digest/sha1'
|
5
|
+
require 'zip'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
require_relative '../external/rubyzip/recursive'
|
9
|
+
|
10
|
+
MAX_UNZIP_SIZE = 1024**2 # 1MiB
|
11
|
+
MAP_VERSION = 1
|
12
|
+
MAP_FILES = [
|
13
|
+
'background.png',
|
14
|
+
'gametiles.txt',
|
15
|
+
'metadata.json'
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
# GameMap class handles the game_map file format
|
19
|
+
class GameMap
|
20
|
+
attr_reader :gametiles, :grass_rows, :ready, :metadata
|
21
|
+
|
22
|
+
def initialize(console, cfg, mapname, callback = nil, checksum = nil)
|
23
|
+
@console = console
|
24
|
+
@cfg = cfg
|
25
|
+
@mapname = mapname
|
26
|
+
@b64_size = -1
|
27
|
+
@b64_data = ''
|
28
|
+
@sha1sum = checksum
|
29
|
+
@gametiles = []
|
30
|
+
# grass_rows
|
31
|
+
# array of hashes containing connected grass tiles
|
32
|
+
# a row of grass from x 0 to x 10 at the height 2 would look like this
|
33
|
+
# {x1: 0, x2: 10, y: 2}
|
34
|
+
@grass_rows = []
|
35
|
+
@ready = false
|
36
|
+
@metadata = nil
|
37
|
+
|
38
|
+
# client
|
39
|
+
@callback = callback
|
40
|
+
@progress = 0
|
41
|
+
@tmpfile = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def checksum
|
45
|
+
@sha1sum
|
46
|
+
end
|
47
|
+
|
48
|
+
def name
|
49
|
+
@mapname
|
50
|
+
end
|
51
|
+
|
52
|
+
# TODO: fix this rubocop
|
53
|
+
# rubocop:disable Naming/AccessorMethodName
|
54
|
+
def set_name(name)
|
55
|
+
raise unless @mapname.nil?
|
56
|
+
|
57
|
+
@mapname = name
|
58
|
+
end
|
59
|
+
|
60
|
+
def size
|
61
|
+
@b64_size
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_size(size)
|
65
|
+
raise unless @b64_size == -1
|
66
|
+
|
67
|
+
@b64_size = size
|
68
|
+
end
|
69
|
+
# rubocop:enable Naming/AccessorMethodName
|
70
|
+
|
71
|
+
def load_gametiles(map_dir)
|
72
|
+
gamefile = "#{map_dir}/gametiles.txt"
|
73
|
+
unless File.exist? gamefile
|
74
|
+
@console.err "could not load gametiles '#{gamefile}'"
|
75
|
+
exit 1
|
76
|
+
end
|
77
|
+
|
78
|
+
is_skip = true
|
79
|
+
@gametiles = []
|
80
|
+
@grass_rows = []
|
81
|
+
File.readlines(gamefile).each_with_index do |data, i|
|
82
|
+
gamerow = data[0..-2] # cut off newline
|
83
|
+
is_skip = !is_skip if gamerow =~ /\+-+\+/
|
84
|
+
gamerow = gamerow.match(/\|(.*)\|/)
|
85
|
+
next if gamerow.nil?
|
86
|
+
|
87
|
+
gamerow = gamerow[1]
|
88
|
+
next if is_skip
|
89
|
+
|
90
|
+
if gamerow.length != MAP_WIDTH
|
91
|
+
@console.err "invalid gametiles row=#{i} size=#{gamerow.length}/#{MAP_WIDTH}"
|
92
|
+
exit 1
|
93
|
+
end
|
94
|
+
@gametiles << gamerow
|
95
|
+
end
|
96
|
+
if @gametiles.length != MAP_HEIGHT
|
97
|
+
@console.err "invalid gametiles rows=#{@gametiles.length}/#{MAP_HEIGHT}"
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
y = 0
|
101
|
+
grass = {}
|
102
|
+
@gametiles.each do |gamerow|
|
103
|
+
x = 0
|
104
|
+
gamerow.chars.each do |tile|
|
105
|
+
if tile == 'i'
|
106
|
+
grass[:x2] = x * TILE_SIZE + TILE_SIZE
|
107
|
+
grass[:y] = y * TILE_SIZE + TILE_SIZE / 2 + 2
|
108
|
+
grass[:x1] = x * TILE_SIZE if grass[:x1].nil?
|
109
|
+
else
|
110
|
+
@grass_rows.push(grass) unless grass == {}
|
111
|
+
grass = {}
|
112
|
+
end
|
113
|
+
x += 1
|
114
|
+
end
|
115
|
+
y += 1
|
116
|
+
end
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
def load_metadata(map_dir)
|
121
|
+
metafile = "#{map_dir}/metadata.json"
|
122
|
+
unless File.exist? metafile
|
123
|
+
@console.err "could not load gametiles '#{metafile}'"
|
124
|
+
exit 1
|
125
|
+
end
|
126
|
+
|
127
|
+
@metadata = JSON.parse(File.read(metafile))
|
128
|
+
if @metadata['chichilku3-map-version'] != MAP_VERSION
|
129
|
+
@console.err "Failed to load map '#{@metadata['name']}':"
|
130
|
+
@console.err " Expected map version '#{MAP_VERSION}' but got '#{@metadata['chichilku3-map-version']}'"
|
131
|
+
exit 1
|
132
|
+
end
|
133
|
+
@console.log "loaded map '#{@metadata['name']}' (#{@metadata['version']}) by #{@metadata['authors'].join(',')}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def load_data(map_dir)
|
137
|
+
load_gametiles(map_dir)
|
138
|
+
load_metadata(map_dir)
|
139
|
+
@ready = true
|
140
|
+
end
|
141
|
+
|
142
|
+
def death?(x, y)
|
143
|
+
{ x: x, y: y } if @gametiles[y][x] == 'X'
|
144
|
+
end
|
145
|
+
|
146
|
+
def collision?(x, y)
|
147
|
+
{ x: x, y: y } if @gametiles[y][x] == 'O'
|
148
|
+
end
|
149
|
+
|
150
|
+
def grass?(x, y)
|
151
|
+
{ x: x, y: y } if @gametiles[y][x] == 'i'
|
152
|
+
end
|
153
|
+
|
154
|
+
# SERVER
|
155
|
+
|
156
|
+
def prepare_upload
|
157
|
+
return if @mapname == '' || @mapname.nil?
|
158
|
+
|
159
|
+
map_dir = "#{@cfg.chichilku3_dir}maps/#{@mapname}"
|
160
|
+
unless File.directory? map_dir
|
161
|
+
@console.err "failed to load map '#{@mapname}' (directory not found)"
|
162
|
+
exit 1
|
163
|
+
end
|
164
|
+
unless File.exist? "#{map_dir}/background.png"
|
165
|
+
@console.err "failed to load map '#{@mapname}' (no background.png)"
|
166
|
+
exit 1
|
167
|
+
end
|
168
|
+
unless File.exist? "#{map_dir}/gametiles.txt"
|
169
|
+
@console.err "failed to load map '#{@mapname}' (no gametiles.txt)"
|
170
|
+
exit 1
|
171
|
+
end
|
172
|
+
load_data(map_dir)
|
173
|
+
zip
|
174
|
+
encode
|
175
|
+
end
|
176
|
+
|
177
|
+
def zip
|
178
|
+
map_dir = "#{@cfg.chichilku3_dir}maps/#{@mapname}"
|
179
|
+
map_zip = "#{@cfg.chichilku3_dir}maps/#{@mapname}.zip"
|
180
|
+
File.delete map_zip if File.exist? map_zip
|
181
|
+
|
182
|
+
@console.log "archiving map '#{map_zip}' ..."
|
183
|
+
Zip::File.open(map_zip, Zip::File::CREATE) do |zipfile|
|
184
|
+
MAP_FILES.each do |filename|
|
185
|
+
filepath = File.join(map_dir, filename)
|
186
|
+
unless File.exist? filepath
|
187
|
+
@console.err "failed to zip map '#{@mapname}' missing file:"
|
188
|
+
@console.err filepath
|
189
|
+
exit 1
|
190
|
+
end
|
191
|
+
zipfile.add(filename, filepath)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def encode
|
197
|
+
rawfile = "#{@cfg.chichilku3_dir}maps/#{@mapname}.zip"
|
198
|
+
@console.log "encoding map archive '#{@mapname}' ..."
|
199
|
+
File.open(rawfile, 'rb') do |map_png|
|
200
|
+
raw_content = map_png.read
|
201
|
+
@sha1sum = Digest::SHA1.hexdigest raw_content
|
202
|
+
encodefile = "#{@cfg.chichilku3_dir}maps_b64/#{@mapname}_#{checksum}.zip"
|
203
|
+
File.open(encodefile, 'wb') do |map_encoded|
|
204
|
+
@b64_data = Base64.encode64(raw_content).delete! "\n"
|
205
|
+
@b64_size = @b64_data.size
|
206
|
+
map_encoded.write(@b64_data)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
@console.log "finished encoding size=#{@b64_size} checksum=#{checksum}"
|
210
|
+
end
|
211
|
+
|
212
|
+
def get_data(offset, size)
|
213
|
+
return nil if @mapname == '' || @mapname.nil?
|
214
|
+
|
215
|
+
if offset + size > @b64_size
|
216
|
+
@b64_data[offset..-1].ljust(size, ' ')
|
217
|
+
else
|
218
|
+
@b64_data[offset...offset + size]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# CLIENT
|
223
|
+
|
224
|
+
def dl_path
|
225
|
+
"#{@cfg.chichilku3_dir}downloadedmaps/#{@mapname}_#{checksum}"
|
226
|
+
end
|
227
|
+
|
228
|
+
def prepare_download
|
229
|
+
@tmpfile = "#{@cfg.chichilku3_dir}tmp/#{@mapname}"
|
230
|
+
File.delete @tmpfile if File.exist? @tmpfile
|
231
|
+
end
|
232
|
+
|
233
|
+
def download(data)
|
234
|
+
data.strip!
|
235
|
+
@progress += data.size
|
236
|
+
@console.dbg "downloading #{@progress} / #{@b64_size} ..."
|
237
|
+
IO.write(@tmpfile, data, mode: 'a')
|
238
|
+
if @progress >= @b64_size
|
239
|
+
@console.log 'finished download'
|
240
|
+
@callback.call(load)
|
241
|
+
end
|
242
|
+
@progress
|
243
|
+
end
|
244
|
+
|
245
|
+
def found?
|
246
|
+
File.directory? dl_path
|
247
|
+
end
|
248
|
+
|
249
|
+
def unzip
|
250
|
+
map_archive = "#{dl_path}.zip"
|
251
|
+
map_dir = dl_path
|
252
|
+
FileUtils.mkdir_p map_dir
|
253
|
+
Dir.chdir map_dir do
|
254
|
+
Zip::File.open(map_archive) do |zip_file|
|
255
|
+
zip_file.each do |entry|
|
256
|
+
@console.log "extracting '#{entry.name}' ...'"
|
257
|
+
raise 'File too large when extracted' if entry.size > MAX_UNZIP_SIZE
|
258
|
+
|
259
|
+
entry.extract
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
File.delete map_archive if File.exist? map_archive
|
264
|
+
map_dir
|
265
|
+
end
|
266
|
+
|
267
|
+
def load
|
268
|
+
outfile = "#{dl_path}.zip"
|
269
|
+
@console.log 'converting downloaded map ...'
|
270
|
+
File.open(@tmpfile, 'rb') do |map_encoded|
|
271
|
+
File.open(outfile, 'wb') do |map_png|
|
272
|
+
map_png.write(
|
273
|
+
Base64.decode64(map_encoded.read)
|
274
|
+
)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
unzip
|
278
|
+
end
|
279
|
+
end
|
data/lib/share/math.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
SIDE_LEFT = -1
|
4
|
+
SIDE_RIGHT = 1
|
5
|
+
|
6
|
+
##
|
7
|
+
# Checks wether a given point is closer to the left or right step of a given interval
|
8
|
+
#
|
9
|
+
# @return -1 for left and 1 for right
|
10
|
+
# @param interval [Integer] interval size.
|
11
|
+
# @param point [Integer] point to check.
|
12
|
+
def closest_interval_side(interval, point)
|
13
|
+
(point % interval) * 2 < interval ? SIDE_LEFT : SIDE_RIGHT
|
14
|
+
end
|