grueserve 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.
Files changed (117) hide show
  1. data/bin/grueserve +6 -0
  2. data/bin/grueserve_path2lines +59 -0
  3. data/example/attributes.yaml +28 -0
  4. data/example/map.gif +0 -0
  5. data/example/sprites/black_soldier_dead.gif +0 -0
  6. data/example/sprites/black_soldier_dodging_0PI1_1.gif +0 -0
  7. data/example/sprites/black_soldier_dodging_0PI1_2.gif +0 -0
  8. data/example/sprites/black_soldier_dodging_0PI1_3.gif +0 -0
  9. data/example/sprites/black_soldier_dodging_1PI1_1.gif +0 -0
  10. data/example/sprites/black_soldier_dodging_1PI1_2.gif +0 -0
  11. data/example/sprites/black_soldier_dodging_1PI1_3.gif +0 -0
  12. data/example/sprites/black_soldier_dodging_1PI2_1.gif +0 -0
  13. data/example/sprites/black_soldier_dodging_1PI2_2.gif +0 -0
  14. data/example/sprites/black_soldier_dodging_1PI2_3.gif +0 -0
  15. data/example/sprites/black_soldier_dodging_3PI2_1.gif +0 -0
  16. data/example/sprites/black_soldier_dodging_3PI2_2.gif +0 -0
  17. data/example/sprites/black_soldier_dodging_3PI2_3.gif +0 -0
  18. data/example/sprites/black_soldier_standing_0PI1_1.gif +0 -0
  19. data/example/sprites/black_soldier_standing_0PI1_2.gif +0 -0
  20. data/example/sprites/black_soldier_standing_0PI1_3.gif +0 -0
  21. data/example/sprites/black_soldier_standing_1PI1_1.gif +0 -0
  22. data/example/sprites/black_soldier_standing_1PI1_2.gif +0 -0
  23. data/example/sprites/black_soldier_standing_1PI1_3.gif +0 -0
  24. data/example/sprites/black_soldier_standing_1PI2_1.gif +0 -0
  25. data/example/sprites/black_soldier_standing_1PI2_2.gif +0 -0
  26. data/example/sprites/black_soldier_standing_1PI2_3.gif +0 -0
  27. data/example/sprites/black_soldier_standing_3PI2_1.gif +0 -0
  28. data/example/sprites/black_soldier_standing_3PI2_2.gif +0 -0
  29. data/example/sprites/black_soldier_standing_3PI2_3.gif +0 -0
  30. data/example/sprites/brown_soldier_dead.gif +0 -0
  31. data/example/sprites/brown_soldier_dodging_0PI1_1.gif +0 -0
  32. data/example/sprites/brown_soldier_dodging_0PI1_2.gif +0 -0
  33. data/example/sprites/brown_soldier_dodging_0PI1_3.gif +0 -0
  34. data/example/sprites/brown_soldier_dodging_1PI1_1.gif +0 -0
  35. data/example/sprites/brown_soldier_dodging_1PI1_2.gif +0 -0
  36. data/example/sprites/brown_soldier_dodging_1PI1_3.gif +0 -0
  37. data/example/sprites/brown_soldier_dodging_1PI2_1.gif +0 -0
  38. data/example/sprites/brown_soldier_dodging_1PI2_2.gif +0 -0
  39. data/example/sprites/brown_soldier_dodging_1PI2_3.gif +0 -0
  40. data/example/sprites/brown_soldier_dodging_3PI2_1.gif +0 -0
  41. data/example/sprites/brown_soldier_dodging_3PI2_2.gif +0 -0
  42. data/example/sprites/brown_soldier_dodging_3PI2_3.gif +0 -0
  43. data/example/sprites/brown_soldier_standing_0PI1_1.gif +0 -0
  44. data/example/sprites/brown_soldier_standing_0PI1_2.gif +0 -0
  45. data/example/sprites/brown_soldier_standing_0PI1_3.gif +0 -0
  46. data/example/sprites/brown_soldier_standing_1PI1_1.gif +0 -0
  47. data/example/sprites/brown_soldier_standing_1PI1_2.gif +0 -0
  48. data/example/sprites/brown_soldier_standing_1PI1_3.gif +0 -0
  49. data/example/sprites/brown_soldier_standing_1PI2_1.gif +0 -0
  50. data/example/sprites/brown_soldier_standing_1PI2_2.gif +0 -0
  51. data/example/sprites/brown_soldier_standing_1PI2_3.gif +0 -0
  52. data/example/sprites/brown_soldier_standing_3PI2_1.gif +0 -0
  53. data/example/sprites/brown_soldier_standing_3PI2_2.gif +0 -0
  54. data/example/sprites/brown_soldier_standing_3PI2_3.gif +0 -0
  55. data/example/sprites/explosion_1.gif +0 -0
  56. data/example/sprites/explosion_10.gif +0 -0
  57. data/example/sprites/explosion_11.gif +0 -0
  58. data/example/sprites/explosion_12.gif +0 -0
  59. data/example/sprites/explosion_13.gif +0 -0
  60. data/example/sprites/explosion_14.gif +0 -0
  61. data/example/sprites/explosion_15.gif +0 -0
  62. data/example/sprites/explosion_16.gif +0 -0
  63. data/example/sprites/explosion_17.gif +0 -0
  64. data/example/sprites/explosion_18.gif +0 -0
  65. data/example/sprites/explosion_19.gif +0 -0
  66. data/example/sprites/explosion_2.gif +0 -0
  67. data/example/sprites/explosion_20.gif +0 -0
  68. data/example/sprites/explosion_21.gif +0 -0
  69. data/example/sprites/explosion_22.gif +0 -0
  70. data/example/sprites/explosion_23.gif +0 -0
  71. data/example/sprites/explosion_24.gif +0 -0
  72. data/example/sprites/explosion_25.gif +0 -0
  73. data/example/sprites/explosion_26.gif +0 -0
  74. data/example/sprites/explosion_27.gif +0 -0
  75. data/example/sprites/explosion_3.gif +0 -0
  76. data/example/sprites/explosion_4.gif +0 -0
  77. data/example/sprites/explosion_5.gif +0 -0
  78. data/example/sprites/explosion_6.gif +0 -0
  79. data/example/sprites/explosion_7.gif +0 -0
  80. data/example/sprites/explosion_8.gif +0 -0
  81. data/example/sprites/explosion_9.gif +0 -0
  82. data/example/sprites/green_soldier_dead.gif +0 -0
  83. data/example/sprites/green_soldier_dodging_0PI1_1.gif +0 -0
  84. data/example/sprites/green_soldier_dodging_0PI1_2.gif +0 -0
  85. data/example/sprites/green_soldier_dodging_0PI1_3.gif +0 -0
  86. data/example/sprites/green_soldier_dodging_1PI1_1.gif +0 -0
  87. data/example/sprites/green_soldier_dodging_1PI1_2.gif +0 -0
  88. data/example/sprites/green_soldier_dodging_1PI1_3.gif +0 -0
  89. data/example/sprites/green_soldier_dodging_1PI2_1.gif +0 -0
  90. data/example/sprites/green_soldier_dodging_1PI2_2.gif +0 -0
  91. data/example/sprites/green_soldier_dodging_1PI2_3.gif +0 -0
  92. data/example/sprites/green_soldier_dodging_3PI2_1.gif +0 -0
  93. data/example/sprites/green_soldier_dodging_3PI2_2.gif +0 -0
  94. data/example/sprites/green_soldier_dodging_3PI2_3.gif +0 -0
  95. data/example/sprites/green_soldier_standing_0PI1_1.gif +0 -0
  96. data/example/sprites/green_soldier_standing_0PI1_2.gif +0 -0
  97. data/example/sprites/green_soldier_standing_0PI1_3.gif +0 -0
  98. data/example/sprites/green_soldier_standing_1PI1_1.gif +0 -0
  99. data/example/sprites/green_soldier_standing_1PI1_2.gif +0 -0
  100. data/example/sprites/green_soldier_standing_1PI1_3.gif +0 -0
  101. data/example/sprites/green_soldier_standing_1PI2_1.gif +0 -0
  102. data/example/sprites/green_soldier_standing_1PI2_2.gif +0 -0
  103. data/example/sprites/green_soldier_standing_1PI2_3.gif +0 -0
  104. data/example/sprites/green_soldier_standing_3PI2_1.gif +0 -0
  105. data/example/sprites/green_soldier_standing_3PI2_2.gif +0 -0
  106. data/example/sprites/green_soldier_standing_3PI2_3.gif +0 -0
  107. data/ext/extconf.rb +93 -0
  108. data/ext/grueserve_ext.c +2083 -0
  109. data/lib/grueserve.rb +28 -0
  110. data/lib/grueserve/application.rb +72 -0
  111. data/lib/grueserve/client.rb +339 -0
  112. data/lib/grueserve/debuggable.rb +114 -0
  113. data/lib/grueserve/map.rb +178 -0
  114. data/lib/grueserve/rated.rb +44 -0
  115. data/lib/grueserve/server.rb +201 -0
  116. data/lib/grueserve/soldier.rb +181 -0
  117. metadata +179 -0
@@ -0,0 +1,178 @@
1
+ # grueserve - a game server in c and ruby for the gruesome game
2
+ # Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ require 'rubygems'
19
+ require 'pathname'
20
+ require 'yaml'
21
+ require 'geo'
22
+
23
+ module Grueserve
24
+
25
+ class Map
26
+
27
+ def self.blocks?(distance, profile)
28
+ return true if profile >= 1
29
+ return false if profile <= 0
30
+ return rand < (profile - (1.0 / ((distance / 100) + (1.0 / profile))))
31
+ end
32
+
33
+ include Debuggable
34
+
35
+ attr_reader :teams, :application
36
+
37
+ def initialize(path, application = nil)
38
+ @path = path
39
+ @application = application
40
+ @attributes = YAML.load(@path.join("attributes.yaml").read)
41
+ init_lines("movement_obstacles")
42
+ init_lines("visibility_obstacles")
43
+ init_lines("bullet_obstacles")
44
+ init_lines("hearing_obstacles")
45
+ init_lines("fortifications")
46
+ init_areas("speed_areas")
47
+ init_areas("protection_areas")
48
+ init_areas("hidden_areas")
49
+ @hearing_distance_cutoff = (@attributes["hearing_distance_cutoff"] || 100).to_f
50
+ @viewing_distance_limit = (@attributes["viewing_distance_limit"] || 1000).to_f
51
+ @teams = @attributes["teams"].split
52
+ @start_points = @teams.inject({}) do |sum, team|
53
+ sum.merge(team => @attributes["#{team}_start_points"].split.collect do |point_string|
54
+ coords = point_string.split(/,/)
55
+ Geo::Point.new(coords.first.to_f, coords.last.to_f)
56
+ end)
57
+ end
58
+ end
59
+
60
+ def size_of_team(team)
61
+ members = 0
62
+ each_client do |client|
63
+ members += 1 if client.team == team
64
+ end
65
+ return members
66
+ end
67
+
68
+ def smallest_team
69
+ return @teams.sort do |team1,team2|
70
+ size_of_team(team1) <=> size_of_team(team2)
71
+ end.first
72
+ end
73
+
74
+ def send_binaries(client)
75
+ Pathname.glob(@path.join("sprites").join("**").join("*")).each do |sprite_frame|
76
+ client.send_bin(sprite_frame.basename, sprite_frame.read)
77
+ end
78
+ client.send_bin("map", img.read)
79
+ end
80
+
81
+ def protection_factor(location)
82
+ rval = 1.0
83
+ if area = protection_areas.first_container(location)
84
+ rval *= self[area] / 1000.0
85
+ end
86
+ return rval
87
+ end
88
+
89
+ def start_point(team, not_point = nil)
90
+ points_to_choose_from = @start_points[team].clone
91
+ points_to_choose_from.delete(not_point) if not_point && points_to_choose_from.size > 1
92
+ the_point = points_to_choose_from[rand(points_to_choose_from.size)]
93
+ return Geo::Point.new(the_point.x, the_point.y)
94
+ end
95
+
96
+ def img
97
+ @path.join(@attributes["map"])
98
+ end
99
+
100
+ def bullet_intersection(line)
101
+ definite_stop = bullet_obstacles.closest_intersection(line)
102
+ line.p2 = definite_stop.first if definite_stop
103
+ fortifications.intersections(line).sort do |a,b|
104
+ a.first.to(line.p1).abs <=> b.first.to(line.p1).abs
105
+ end.each do |possible_stop|
106
+ if self.class.blocks?(line.p1.to(possible_stop.first).abs, self[possible_stop.last] / 1000.0)
107
+ return possible_stop.first
108
+ end
109
+ end
110
+ return line.p2
111
+ end
112
+
113
+ def noise_damp(line)
114
+ obstacles_damp = hearing_obstacles.intersections(line).inject(1.0) do |sum, obstacle|
115
+ sum * self[obstacle.last] / 1000.0
116
+ end
117
+ (1.0 / ([(line.abs / @hearing_distance_cutoff),1].max ** 2)) * obstacles_damp
118
+ end
119
+
120
+ private
121
+
122
+ def init_areas(name)
123
+ triangle_set = self.send(name.to_sym)
124
+ if stringData = @attributes[name]
125
+ stringData.split(/:/).each do |segment|
126
+ pointString, valueString = segment.split(/=/)
127
+ value = @attributes["#{name}_default_value"].to_i
128
+ value = valueString.to_i if valueString
129
+ points = pointString.split
130
+ middle = point_from_string(points.shift)
131
+ (points.size - 1).times do |i|
132
+ p1 = points[i]
133
+ p1 = points.first if p1 == "loop"
134
+ p1 = point_from_string(p1)
135
+ p2 = points[i + 1]
136
+ p2 = points.first if p2 == "loop"
137
+ p2 = point_from_string(p2)
138
+ area = Geo::Triangle.new(middle, p1, p2)
139
+ log_debug("adding area #{area.inspect} with value #{value} to @#{name}")
140
+ triangle_set << area
141
+ self[area] = value
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ def init_lines(name)
148
+ line_set = self.send(name.to_sym)
149
+ if stringData = @attributes[name]
150
+ stringData.split(/:/).each do |segment|
151
+ pointString, valueString = segment.split(/=/)
152
+ value = @attributes["#{name}_default_value"].to_i
153
+ value = valueString.to_i if valueString
154
+ points = segment.split
155
+ (points.size - 1).times do |i|
156
+ p1 = points[i]
157
+ p1 = points.first if p1 == "loop"
158
+ p1 = point_from_string(p1)
159
+ p2 = points[i + 1]
160
+ p2 = points.first if p2 == "loop"
161
+ p2 = point_from_string(p2)
162
+ o = Geo::Line.new(p1, p2)
163
+ log_debug("adding obstacle #{o.inspect} with value #{value} to @#{name}")
164
+ line_set << o
165
+ self[o] = value
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ def point_from_string(s)
172
+ x,y = s.split(/,/)
173
+ return Geo::Point.new(x.to_i,y.to_i)
174
+ end
175
+
176
+ end
177
+
178
+ end
@@ -0,0 +1,44 @@
1
+ # grueserve - a game server in c and ruby for the gruesome game
2
+ # Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+
19
+ module Grueserve
20
+
21
+ module Rated
22
+
23
+ class Rate
24
+ def initialize(n)
25
+ @n = n
26
+ @times = {}
27
+ end
28
+ def times_a_second(&block)
29
+ time = (@times[:second] ||= Time.now.to_f)
30
+ if Time.now.to_f - time > 1.0 / @n
31
+ @times[:second] = Time.now.to_f
32
+ yield
33
+ end
34
+ end
35
+ end
36
+
37
+ def max_rate_of(n)
38
+ @rates ||= {}
39
+ @rates[n] ||= Rate.new(n)
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,201 @@
1
+ # grueserve - a game server in c and ruby for the gruesome game
2
+ # Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ require 'rubygems'
19
+ require 'socket'
20
+ require 'pp'
21
+ require 'monitor'
22
+ require 'xml/libxml'
23
+
24
+ module Grueserve
25
+
26
+ class Server
27
+
28
+ module ClientSocket
29
+
30
+ class PolicyFileRequest < RuntimeError
31
+ end
32
+
33
+ include MonitorMixin
34
+ include Debuggable
35
+
36
+ attr_accessor :buffer, :bufferSize, :client, :server
37
+
38
+ def init_client_socket(client, server)
39
+ mon_initialize
40
+ @client = client
41
+ @server = server
42
+ @buffer = ""
43
+ @bufferSize = 0
44
+ end
45
+
46
+ def readTo(char)
47
+ rval = ""
48
+ while (next_char = self.read_nonblock(1)) != char
49
+ rval += next_char
50
+ if rval == "<policy-file-request/>\0"
51
+ log_debug("Received policy file request #{rval.inspect}")
52
+ raise PolicyFileRequest.new
53
+ end
54
+ end
55
+ return rval
56
+ end
57
+
58
+ def handle_input
59
+ begin
60
+ @bufferSize = readTo(":").to_i if @bufferSize == 0
61
+ log_trace("Expecting package of size #{@bufferSize}")
62
+ gotten = self.read_nonblock(@bufferSize - @buffer.size)
63
+ log_trace("Received buffer #{gotten.inspect}")
64
+ @buffer += gotten
65
+ if @buffer.size == @bufferSize
66
+ log_debug("Received complete buffer from #{self}: #{@buffer.inspect}")
67
+ doc = XML::Parser.string(@buffer).parse
68
+ client.handle_input(doc)
69
+ @buffer = ""
70
+ @bufferSize = 0
71
+ end
72
+ rescue Exception => e
73
+ server.kill_socket(self)
74
+ raise e
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ include Debuggable
81
+
82
+ def initialize(options = {})
83
+ @port = options.delete(:port) || 25999
84
+ raise "#{self.class}.new requires an :application argument." unless options.include?(:application)
85
+ @application = options.delete(:application)
86
+ @frequency = options.delete(:frequency) || 100
87
+ raise "Unknown options to #{self.class}: #{options.inspect}" unless options.empty?
88
+ @sockets = []
89
+ @sockets_to_close = Set.new
90
+ end
91
+
92
+ def start!
93
+ start_updating!
94
+ start_listening!
95
+ start_serving!
96
+ end
97
+
98
+ def start_updating!
99
+ @updater ||= nil
100
+ @updater.kill if @updater.respond_to?(:kill)
101
+ @updater = Thread.new do
102
+ counter = 0
103
+ timer = Time.now
104
+ sleep_sum = 0
105
+ loop do
106
+ begin
107
+ start_time = Time.now
108
+ @application.map.update!
109
+ if (diff = (1.0 / @frequency) - ((stop_time = Time.now).to_f - start_time.to_f)) > 0
110
+ sleep_sum += diff
111
+ sleep(diff)
112
+ end
113
+ counter += 1
114
+ if Time.now - timer > 10
115
+ avg_frequency = counter / (Time.now.to_f - timer.to_f)
116
+ avg_sleep = sleep_sum / (Time.now.to_f - timer.to_f)
117
+ log_info("Avg frequency: #{avg_frequency}, sleeping #{(avg_sleep * 100).to_i}% of the time")
118
+ report
119
+ reset
120
+ timer = Time.now
121
+ counter = 0
122
+ sleep_sum = 0
123
+ end
124
+ rescue Exception => e
125
+ log_warn(e)
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def kill_socket(socket)
132
+ socket.client.shutdown
133
+ @sockets.delete(socket)
134
+ @sockets_to_close << socket
135
+ end
136
+
137
+ def start_listening!
138
+ @listener ||= nil
139
+ @listener.kill if @listener.respond_to?(:kill)
140
+ @listener = Thread.new do
141
+ loop do
142
+ begin
143
+ to_close = @sockets_to_close.to_a
144
+ while (socket = to_close.shift)
145
+ socket.close unless socket.closed?
146
+ end
147
+ read_ready, write_read, errors = select(@sockets, nil, @sockets, 1)
148
+ unless errors.nil?
149
+ errors.each do |socket|
150
+ log_warn("Going to drop #{socket} from #{@sockets.inspect} since it got an error")
151
+ kill_socket(socket)
152
+ end
153
+ end
154
+ unless read_ready.nil?
155
+ read_ready.each do |socket|
156
+ if socket.eof?
157
+ log_info("Going to drop #{socket} from #{@sockets.inspect} since it is closed")
158
+ kill_socket(socket)
159
+ else
160
+ begin
161
+ socket.handle_input
162
+ rescue ClientSocket::PolicyFileRequest => e
163
+ log_info("Got policy file request from #{socket.inspect}")
164
+ socket.client.handle_policy_request
165
+ end
166
+ end
167
+ end
168
+ end
169
+ rescue Exception => e
170
+ log_warn(e)
171
+ end
172
+ end
173
+ end
174
+ log_debug("Started listener")
175
+ end
176
+
177
+ def start_serving!
178
+ log_debug("Starting serving")
179
+ @server ||= nil
180
+ @server.close if @server.respond_to?(:close)
181
+ @server = TCPServer.new(@port)
182
+ loop do
183
+ begin
184
+ socket = @server.accept_nonblock
185
+ socket.extend(ClientSocket)
186
+ socket.init_client_socket(Client.new(:socket => socket, :application => @application), self)
187
+ log_info("Received #{socket}, adding it to #{@sockets.inspect}")
188
+ @sockets << socket
189
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR => e
190
+ log_trace("Got TCPServer error: #{e}")
191
+ IO.select([@server])
192
+ retry
193
+ rescue Exception => e
194
+ log_warn(e)
195
+ end
196
+ end
197
+ end
198
+
199
+ end
200
+
201
+ end
@@ -0,0 +1,181 @@
1
+ # grueserve - a game server in c and ruby for the gruesome game
2
+ # Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ module Grueserve
19
+
20
+ module Soldier
21
+
22
+ class Aim
23
+ attr_accessor :point, :precision
24
+ def initialize(point, precision)
25
+ @point = point
26
+ @precision = precision
27
+ end
28
+ end
29
+
30
+ RELOAD = "RELOAD"
31
+
32
+ def self.extend_object(o)
33
+ super
34
+ o.init_soldier
35
+ end
36
+
37
+ def init_soldier
38
+ self.behaviour = "soldier"
39
+ @reloading = false
40
+ @aim = nil
41
+ subscribe("LBUTTON", :handle_lbutton)
42
+ subscribe("RELOAD", :handle_reload)
43
+ end
44
+
45
+ def refill
46
+ super
47
+ self.ammo = max_ammo
48
+ end
49
+
50
+ def ammo
51
+ @ammo
52
+ end
53
+
54
+ def ammo=(x)
55
+ @ammo = x
56
+ self.extra_status = " Ammo: #{@ammo}"
57
+ end
58
+
59
+ def max_ammo
60
+ 32
61
+ end
62
+
63
+ def base_grenade_precision_factor
64
+ 2
65
+ end
66
+
67
+ def grenade_precision_delta
68
+ 0.1
69
+ end
70
+
71
+ def grenade_max_precision_factor
72
+ 5
73
+ end
74
+
75
+ def reload_time
76
+ 3
77
+ end
78
+
79
+ def throwing_speed
80
+ 100
81
+ end
82
+
83
+ def max_throwing_distance
84
+ 200
85
+ end
86
+
87
+ def reload
88
+ if !@reloading && @aim.nil
89
+ @reloading = true
90
+ broadcast_noise("reload", body.origo)
91
+ thread_with_rescue do
92
+ sleep(reload_time)
93
+ self.ammo = max_ammo
94
+ @reloading = false
95
+ end
96
+ end
97
+ end
98
+
99
+ def throw_grenade
100
+ this_fuzz_factor = @aim.point.abs / @aim.precision
101
+ loc = body.origo + Geo::Point.new(@aim.point.x + (this_fuzz_factor * fuzz),
102
+ @aim.point.y + (this_fuzz_factor * fuzz))
103
+ send_throw(body.origo.to(loc), throwing_speed)
104
+ @aim = nil
105
+ end
106
+
107
+ def handle_lbutton(command)
108
+ unless @reloading
109
+ if command.nil?
110
+ unless @aim.nil?
111
+ throw_grenade
112
+ end
113
+ else
114
+ if current_command.include?("SHIFT")
115
+ max_rate_of(10).times_a_second do
116
+ if @aim.nil?
117
+ new_point = Geo::Point.new(command["x"].to_i, command["y"].to_i) - body.origo
118
+ new_point.abs = max_throwing_distance if new_point.abs > max_throwing_distance
119
+ @aim = Aim.new(new_point, base_grenade_precision_factor)
120
+ else
121
+ @aim.precision += grenade_precision_delta unless @aim.precision > grenade_max_precision_factor
122
+ end
123
+ aim_point = body.origo + @aim.point
124
+ send_aim(aim_point.x, aim_point.y, @aim.point.abs / @aim.precision)
125
+ end
126
+ else
127
+ if @aim.nil?
128
+ if ammo > 0
129
+ max_rate_of(8).times_a_second do
130
+ shot_line = body.origo.to(command["x"].to_i, command["y"].to_i)
131
+ this_fuzz_factor = shoot_fuzz_factor
132
+ angle = fuzz * this_fuzz_factor + shot_line.angle
133
+ send_aim(command["x"], command["y"], Math.tan(this_fuzz_factor) * shot_line.abs)
134
+ fire_bullet(angle)
135
+ self.ammo -= 1
136
+ end
137
+ else
138
+ max_rate_of(1).times_a_second do
139
+ broadcast_noise("dry_shot", body.origo)
140
+ end
141
+ end
142
+ else
143
+ throw_grenade
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ def handle_reload(command)
151
+ unless command.nil?
152
+ reload
153
+ end
154
+ end
155
+
156
+ def damage
157
+ 2
158
+ end
159
+
160
+ def shoot_fuzz_factor
161
+ rval = 1.0
162
+ if moving?
163
+ if state == :dodging
164
+ rval *= 4
165
+ else
166
+ rval *= 2
167
+ end
168
+ else
169
+ rval *= 0.5 if state == :dodging
170
+ end
171
+ return rval / precision
172
+ end
173
+
174
+ def precision
175
+ 30.0
176
+ end
177
+
178
+ end
179
+
180
+
181
+ end