rbot 0.1.9

Library for programming robots in Bot Beats.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
use crate::errors::MessageError;
use crate::hostfn;
use crate::rotations;
use crate::rotations::transform_rotation_to_component;
use rbot_messages::messages as msg;
use rbot_messages::MessageType;

/// Fires the component with the specified `component_id`.
///
/// If `sticky` is `true`, it will keep firing the component whenever the cooldown is ready.
/// Otherwise, it will fire the component once.
///
/// # Arguments
///
/// * `component_id` - The identifier of the component to fire.
/// * `sticky` - A flag indicating whether to continuously fire the component (`true`) or fire it once (`false`).
///
/// # Returns
///
/// Whether or not the command sent was recived successfully or not.
///
/// # Examples
///
/// ```
/// // Trigger the first component to fire (if cooldown is ready).
/// let result = rbot::use_component(0, false);
///
/// // If something went wrong in the communication with the server, print it
/// // out to the game console.
/// if result.err {
///     rbot::print("Failed communicating with the server when calling `use_component`.")
/// }
/// ```
pub fn use_component(component_id: i32, sticky: bool) -> Result<(), MessageError> {
    let sticky = match sticky {
        false => 0,
        _ => 1,
    };
    let msg_use = msg::MsgUse {
        component_id,
        sticky,
    };
    let response = hostfn::send_message(&msg_use);

    match response {
        MessageType::Error(m) => Err(MessageError::BadCommand(m.error_code)),
        _ => Ok(()),
    }
}

/// Sets the velocity and direction for the robot's traversal.
///
/// This function specifies the direction and speed at which the robot should
/// move. The `x` and `y` parameters represent components of a 2D vector that
/// determines the direction of movement. These values will be normalized to
/// create a unit vector pointing in the specified direction.
///
/// The `speed` parameter is a decimal number between 0 and 1, indicating the
/// magnitude of the velocity vector. A `speed` value of 0 corresponds to no
/// movement, while a `speed` value of 1 represents maximum speed in the
/// specified direction. The speed will be clamped between 0 and 1 on the game
/// server.
///
/// # Arguments
///
/// * `x` - The X-component of the direction vector.
/// * `y` - The Y-component of the direction vector.
/// * `speed` - The speed of traversal (between 0 and 1).
///
/// # Returns
///
/// Returns `Ok(())` if the velocity command was sent successfully.
///
/// # Examples
///
/// ```
/// let result = rbot::velocity(1.0, 0.5, 0.8);
///
/// // If something went wrong in the communication with the server, print it
/// // out to the game console.
/// if result.err {
///     rbot::print("Failed communicating with the server when calling `velocity`.")
/// }
/// ```
pub fn velocity(x: f32, y: f32, speed: f32) -> Result<(), MessageError> {
    let msg_use = msg::MsgVelocity { x, y, speed };
    let response = hostfn::send_message(&msg_use);
    match response {
        MessageType::Error(m) => Err(MessageError::BadCommand(m.error_code)),
        _ => Ok(()),
    }
}

/// Rotates the robot to a specified angle in degrees.
///
/// This function rotates the robot to the desired angle measured in degrees.
/// It is generally discouraged to call this function frequently with small rotation
/// updates, as it may reduce the speed and efficiency of the robot's rotation.
///
/// # Arguments
///
/// * `angle` - The target angle in degrees to which the robot should rotate.
///
/// # Returns
///
/// Returns `Ok(())` if the rotation command was sent successfully.
///
/// # Examples
///
/// ```
/// let result = rbot::rotate(90.0);
///
/// // If something went wrong in the communication with the server, print it
/// // out to the game console.
/// if result.err {
///     rbot::print("Failed communicating with the server when calling `use_component`.")
/// }
/// ```
pub fn rotate(angle: f32) -> Result<(), MessageError> {
    let msg_use = msg::MsgAngle { angle };
    let response = hostfn::send_message(&msg_use);
    match response {
        MessageType::Error(m) => Err(MessageError::BadCommand(m.error_code)),
        _ => Ok(()),
    }
}

/// Checks if the robot is at a specified rotation angle within a tolerance range.
///
/// This function determines whether the robot's current rotation angle matches
/// the target `angle` within a specified tolerance `slack`. The `slack` parameter
/// allows for a larger offset from the target angle, enabling flexibility in the
/// angle comparison.
///
/// A recommended value for `slack` is 0.5 degrees if you want to consider the robot
/// at the desired angle within a small tolerance.
///
/// # Arguments
///
/// * `component_id` - The identifier of the robot component to check.
/// * `angle` - The target rotation angle in degrees.
/// * `slack` - The allowed tolerance (slack) in degrees for the angle comparison.
///
/// # Returns
///
/// Returns `true` if the robot's rotation angle is within the target angle ±
/// `slack`, otherwise returns `false`. If any communication to the game server
/// fails the function returns an MessageError.
///
/// # Examples
///
/// ```
/// let is_at_angle = at_rotation(0, 90, 0.5)?;
/// rbot::print(&format!("Component 0 is at angle 90 degrees: {is_at_angle}"));
/// ```
pub fn at_rotation(component_id: i32, angle: f32, slack: f32) -> Result<bool, MessageError> {
    let target_angle = rotations::transform_rotation_to_component(component_id, angle);
    let current_rotation = state()?.angle;
    let angle_difference = rotations::angle_distance(target_angle, current_rotation);
    Ok(angle_difference < slack)
}

/// Aims a robot component towards a specified angle in a 2D coordinate system.
///
/// This function aims the specified robot `component_id` towards the given `angle`
/// within a 2D coordinate system where:
///
/// - The origin (0,0) is at the center of the map.
/// - Y-axis points upwards.
/// - X-axis points to the right.
/// - Angle measurement:
///   - 0 degrees: Towards the right (positive X-axis).
///   - 90 degrees: Towards the top (positive Y-axis).
///   - 180 degrees: Towards the left (negative X-axis).
///   - 270 degrees: Towards the bottom (negative Y-axis).
///
/// # Arguments
///
/// * `component_id` - The identifier of the robot component to aim.
/// * `angle` - The target angle in degrees (0 to 360), where 0 degrees points towards the right.
///
/// # Returns
///
/// Returns `Ok(())` if the aiming operation is successful, or an error of type
/// `MessageError` if there was a problem during communication with the game
/// server.
///
/// # Examples
///
/// ```
/// let result = rbot::aim(0, 90);
///
/// // If something went wrong in the communication with the server, print it
/// // out to the game console.
/// if result.err {
///     rbot::print("Failed communicating with the game server.")
/// }
/// ```
pub fn aim(component_id: i32, angle: f32) -> Result<(), MessageError> {
    rotate(transform_rotation_to_component(component_id, angle))
}

/// Aims the robot component towards the specified angle and waits for the
/// aiming process to complete within a tolerance range.
///
/// This function blocks the execution of code until the specified
/// `component_id` is aimed towards the target `angle` within the specified
/// `slack` tolerance range.
///
/// # Arguments
///
/// * `component_id` - The identifier of the robot component to aim.
/// * `angle` - The target angle in degrees (0 to 360), where 0 degrees points towards the right.
/// * `slack` - The allowed tolerance (slack) in degrees for the aiming process.
///
/// # Returns
///
/// Returns `Ok(())` if the aiming process is successful within the specified
/// tolerance range, or an error of type `MessageError` if there was a problem
/// during communication with the game server.
///
/// # Examples
///
/// ```
/// let result = rbot::await_aim(0, 90, 0.5);
///
/// // If something went wrong in the communication with the server, print it
/// // out to the game console.
/// if result.err {
///     rbot::print("Failed communicating with the game server.")
/// }
/// ```
pub fn await_aim(component_id: i32, angle: f32, slack: f32) -> Result<(), MessageError> {
    aim(component_id, angle)?;
    while !at_rotation(component_id, angle, slack)? {
        sleep(0.01);
    }
    Ok(())
}

/// Waits for the specified robot component's cooldown.
///
/// This function blocks the execution of code until the cooldown is ready of
/// the specified `component_id`. After awaiting the cooldown, it becomes
/// possible to call `use_component` and trigger the specified component.
///
/// # Arguments
///
/// * `component_id` - The identifier of the robot component for which to await the cooldown.
///
/// # Returns
///
/// Returns `Ok(())` if the await is successful else an error of type
/// `MessageError` if there was a problem during communication with the game
/// server.
///
/// # Examples
///
/// ```
/// await_component(0)?;
/// use_component(0)?;
/// ```
pub fn await_component(component_id: i32) -> Result<(), MessageError> {
    await_action()?;
    loop {
        let state = component_state(component_id)?;
        if state.cooldown <= 0.0 {
            break;
        }
        if state.health <= 0.0 {
            break;
        }
        sleep(0.01);
    }
    Ok(())
}

/// Awaits until a component is no longer activated.
///
/// This function blocks execution until the specified component is no longer activated,
/// repeatedly checking the component's state and sleeping for a short duration between checks.
///
/// # Parameters
///
/// * `component_id` - The ID of the component to monitor.
///
/// # Returns
///
/// * `Ok(())` - if the component is no longer activated.
/// * `Err(MessageError)` - if an error occurs, with details about the specific error.
///
/// # Errors
///
/// This function can return the following errors:
///
/// * `MessageError::BadCommand` - if there is an issue with the command sent to the game.
/// * `MessageError::InvalidResponse` - if the response from the game is not as expected.
///
/// # Examples
///
/// ```
/// // Wait for the component with ID 0 to no longer be activated.
/// rbot::await_not_activated(0)?;
/// ```
pub fn await_not_activated(component_id: i32) -> Result<(), MessageError> {
    await_action()?;
    while component_state(component_id)?.is_activated {
        sleep(0.01);
    }
    Ok(())
}

/// Retrieves the current state of the robot.
///
/// The robot state includes information such as its angle, velocity, motherboard health,
/// and active buffs on the main board. To extract detailed state information about specific
/// components, use the `component_state` function.
///
/// # Returns
///
/// Returns a `Result` containing `msg::RMsgState` representing the current state of the robot
/// if the retrieval is successful, or an error of type `MessageError` if there was a problem
/// retrieving the robot state.
///
/// # Examples
///
/// ```
/// let robot_state = state()?;
/// ```
pub fn state() -> Result<msg::RMsgState, MessageError> {
    let msg_use = msg::MsgState { value: 0 };
    let response = hostfn::send_message(&msg_use);

    match response {
        MessageType::Error(m) => Err(MessageError::BadCommand(m.error_code)),
        MessageType::RState(m) => Ok(m),
        _ => Err(MessageError::InvalidResponse),
    }
}

/// Retrieves the current status of the specified robot component.
///
/// This function retrieves detailed information about the health, cooldown status,
/// and activation state of the robot's component with id `component_id`.
///
/// # Arguments
///
/// * `component_id` - The identifier of the robot component for which to retrieve the state.
///
/// # Returns
///
/// Returns a `Result` containing `msg::RMsgComponentStatus` representing the current status
/// of the specified robot component if the retrieval is successful, or an error of type `MessageError`
/// if there was a problem retrieving the component state.
///
/// # Examples
///
/// ```
/// let component_state = rbot::component_state(0)?;
/// ```
pub fn component_state(component_id: i32) -> Result<msg::RMsgComponentStatus, MessageError> {
    let msg_comp_state = msg::MsgComponentStatusQuery { component_id };
    let response = hostfn::send_message(&msg_comp_state);

    match response {
        MessageType::Error(m) => Err(MessageError::BadCommand(m.error_code)),
        MessageType::RComponentStatus(m) => Ok(m),
        _ => Err(MessageError::InvalidResponse),
    }
}

/// Pauses the main thread for the specified duration in seconds.
///
/// This function blocks the execution of the main thread, causing it to sleep
/// for the given `seconds` before resuming.
///
/// # Arguments
///
/// * `seconds` - The duration in seconds for which the thread should sleep.
///
/// # Examples
///
/// ```
/// rbot::print("Starting to wait...");
/// rbot::sleep(2.5);
/// rbot::print("Waited for 2.5 seconds.");
/// ```
pub fn sleep(seconds: f32) {
    unsafe { hostfn::bot_sleep(seconds) };
}

/// Generates a pseudo-random floating-point number between 0 (inclusive) and 1 (includive).
///
/// This function returns a random number that falls within the interval [0, 1].
///
/// # Returns
///
/// A random floating-point number in the range [0, 1].
///
/// # Examples
///
/// ```
/// let random_number = random();
/// fn main() {
///     let num = random();
///     println!("Random number between 0 and 1: {}", num);
/// }
///
/// fn random() -> f32 {
///     use rand::Rng;
///     let mut rng = rand::thread_rng();
///     rng.gen::<f32>()
/// }
/// ```
pub fn random() -> f32 {
    unsafe { hostfn::bot_random() }
}

/// Logs a message to the game console in the programming scene.
///
/// This function writes the specified `string` message to a log file that is displayed
/// in the game's console in the programming scene.
///
/// # Arguments
///
/// * `string` - The string message to be logged and displayed in the game console.
///
/// # Examples
///
/// ```
/// rbot::print("Hello World");
/// ```
pub fn print(string: &str) {
    let size = string.len() as i32;
    let bytes_ptr = string.as_ptr() as i32;
    unsafe {
        hostfn::dbg_log(bytes_ptr, size);
    }
}

/// Retrieves the current timestamp in seconds.
///
/// This function returns the current timestamp as a floating-point number representing
/// the number of seconds elapsed since a specific reference time.
///
/// # Returns
///
/// Returns the current timestamp in seconds as a `Result<f32, MessageError>`.
/// If successful, the `Ok` variant contains the timestamp value.
///
/// # Examples
///
/// ```
/// let timestamp = rbot::time()?;
/// ```
pub fn time() -> Result<f32, MessageError> {
    let msg_time = msg::MsgTime { value: 0 };
    let response = hostfn::send_message(&msg_time);

    match response {
        MessageType::Error(m) => Err(MessageError::BadCommand(m.error_code)),
        MessageType::RTime(m) => Ok(m.timestamp),
        _ => Err(MessageError::InvalidResponse),
    }
}

/// Awaits the completion of a game action trigger.
///
/// This function sends a message to the game to await an action, which is crucial
/// for checking the status of a component after it has been used.
///
/// # Returns
///
/// * `Ok(())` - if the action completes successfully.
/// * `Err(MessageError)` - if an error occurs, with details about the specific error.
///
/// # Errors
///
/// This function can return the following errors:
///
/// * `MessageError::BadCommand` - if there is an issue with the command sent to the game.
/// * `MessageError::InvalidResponse` - if the response from the game is not as expected.
///
/// # Examples
///
/// ```
/// // Wait for the component to be ready.
/// rbot::await_component(0)?;
///
/// // Send a command to the game server indicating the robot's intention to
/// // use weapon 0 in the next action loop. This does NOT trigger the weapon
/// // immediately. Checking the cooldown directly after this might still show 0.
/// rbot::use_component(0, false)?;
///
/// // This will pause execution until the robot's action is performed.
/// // After this, the cooldown will no longer be 0, and `is_activated`
/// // will be true if the component has an active time.
/// rbot::await_action()?;
///
/// // Now, retrieving the component state will show the updated cooldown.
/// let component_state = rbot::component_state()?;
/// ```
pub fn await_action() -> Result<(), MessageError> {
    let msg_await_action = msg::MsgAwaitAction { value: 0 };
    let response = hostfn::send_message(&msg_await_action);

    match response {
        MessageType::Error(m) => Err(MessageError::BadCommand(m.error_code)),
        MessageType::Empty(_) => Ok(()),
        _ => Err(MessageError::InvalidResponse),
    }
}