bascenev1lib.actor.spazbot
Bot versions of Spaz.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Bot versions of Spaz.""" 4# pylint: disable=too-many-lines 5 6from __future__ import annotations 7 8import random 9import weakref 10import logging 11from typing import TYPE_CHECKING 12 13import bascenev1 as bs 14from bascenev1lib.actor.spaz import Spaz 15 16if TYPE_CHECKING: 17 from typing import Any, Sequence, Callable 18 from bascenev1lib.actor.flag import Flag 19 20LITE_BOT_COLOR = (1.2, 0.9, 0.2) 21LITE_BOT_HIGHLIGHT = (1.0, 0.5, 0.6) 22DEFAULT_BOT_COLOR = (0.6, 0.6, 0.6) 23DEFAULT_BOT_HIGHLIGHT = (0.1, 0.3, 0.1) 24PRO_BOT_COLOR = (1.0, 0.2, 0.1) 25PRO_BOT_HIGHLIGHT = (0.6, 0.1, 0.05) 26 27 28class SpazBotPunchedMessage: 29 """A message saying a bs.SpazBot got punched. 30 31 Category: **Message Classes** 32 """ 33 34 spazbot: SpazBot 35 """The bs.SpazBot that got punched.""" 36 37 damage: int 38 """How much damage was done to the SpazBot.""" 39 40 def __init__(self, spazbot: SpazBot, damage: int): 41 """Instantiate a message with the given values.""" 42 self.spazbot = spazbot 43 self.damage = damage 44 45 46class SpazBotDiedMessage: 47 """A message saying a bs.SpazBot has died. 48 49 Category: **Message Classes** 50 """ 51 52 spazbot: SpazBot 53 """The SpazBot that was killed.""" 54 55 killerplayer: bs.Player | None 56 """The bascenev1.Player that killed it (or None).""" 57 58 how: bs.DeathType 59 """The particular type of death.""" 60 61 def __init__( 62 self, 63 spazbot: SpazBot, 64 killerplayer: bs.Player | None, 65 how: bs.DeathType, 66 ): 67 """Instantiate with given values.""" 68 self.spazbot = spazbot 69 self.killerplayer = killerplayer 70 self.how = how 71 72 73class SpazBot(Spaz): 74 """A really dumb AI version of bs.Spaz. 75 76 Category: **Bot Classes** 77 78 Add these to a bs.BotSet to use them. 79 80 Note: currently the AI has no real ability to 81 navigate obstacles and so should only be used 82 on wide-open maps. 83 84 When a SpazBot is killed, it delivers a bs.SpazBotDiedMessage 85 to the current activity. 86 87 When a SpazBot is punched, it delivers a bs.SpazBotPunchedMessage 88 to the current activity. 89 """ 90 91 character = 'Spaz' 92 punchiness = 0.5 93 throwiness = 0.7 94 static = False 95 bouncy = False 96 run = False 97 charge_dist_min = 0.0 # When we can start a new charge. 98 charge_dist_max = 2.0 # When we can start a new charge. 99 run_dist_min = 0.0 # How close we can be to continue running. 100 charge_speed_min = 0.4 101 charge_speed_max = 1.0 102 throw_dist_min = 5.0 103 throw_dist_max = 9.0 104 throw_rate = 1.0 105 default_bomb_type = 'normal' 106 default_bomb_count = 3 107 start_cursed = False 108 color = DEFAULT_BOT_COLOR 109 highlight = DEFAULT_BOT_HIGHLIGHT 110 111 def __init__(self) -> None: 112 """Instantiate a spaz-bot.""" 113 super().__init__( 114 color=self.color, 115 highlight=self.highlight, 116 character=self.character, 117 source_player=None, 118 start_invincible=False, 119 can_accept_powerups=False, 120 ) 121 122 # If you need to add custom behavior to a bot, set this to a callable 123 # which takes one arg (the bot) and returns False if the bot's normal 124 # update should be run and True if not. 125 self.update_callback: Callable[[SpazBot], Any] | None = None 126 activity = self.activity 127 assert isinstance(activity, bs.GameActivity) 128 self._map = weakref.ref(activity.map) 129 self.last_player_attacked_by: bs.Player | None = None 130 self.last_attacked_time = 0.0 131 self.last_attacked_type: tuple[str, str] | None = None 132 self.target_point_default: bs.Vec3 | None = None 133 self.held_count = 0 134 self.last_player_held_by: bs.Player | None = None 135 self.target_flag: Flag | None = None 136 self._charge_speed = 0.5 * ( 137 self.charge_speed_min + self.charge_speed_max 138 ) 139 self._lead_amount = 0.5 140 self._mode = 'wait' 141 self._charge_closing_in = False 142 self._last_charge_dist = 0.0 143 self._running = False 144 self._last_jump_time = 0.0 145 146 self._throw_release_time: float | None = None 147 self._have_dropped_throw_bomb: bool | None = None 148 self._player_pts: list[tuple[bs.Vec3, bs.Vec3]] | None = None 149 150 # These cooldowns didn't exist when these bots were calibrated, 151 # so take them out of the equation. 152 self._jump_cooldown = 0 153 self._pickup_cooldown = 0 154 self._fly_cooldown = 0 155 self._bomb_cooldown = 0 156 157 if self.start_cursed: 158 self.curse() 159 160 @property 161 def map(self) -> bs.Map: 162 """The map this bot was created on.""" 163 mval = self._map() 164 assert mval is not None 165 return mval 166 167 def _get_target_player_pt(self) -> tuple[bs.Vec3 | None, bs.Vec3 | None]: 168 """Returns the position and velocity of our target. 169 170 Both values will be None in the case of no target. 171 """ 172 assert self.node 173 botpt = bs.Vec3(self.node.position) 174 closest_dist: float | None = None 175 closest_vel: bs.Vec3 | None = None 176 closest: bs.Vec3 | None = None 177 assert self._player_pts is not None 178 for plpt, plvel in self._player_pts: 179 dist = (plpt - botpt).length() 180 181 # Ignore player-points that are significantly below the bot 182 # (keeps bots from following players off cliffs). 183 if (closest_dist is None or dist < closest_dist) and ( 184 plpt[1] > botpt[1] - 5.0 185 ): 186 closest_dist = dist 187 closest_vel = plvel 188 closest = plpt 189 if closest_dist is not None: 190 assert closest_vel is not None 191 assert closest is not None 192 return ( 193 bs.Vec3(closest[0], closest[1], closest[2]), 194 bs.Vec3(closest_vel[0], closest_vel[1], closest_vel[2]), 195 ) 196 return None, None 197 198 def set_player_points(self, pts: list[tuple[bs.Vec3, bs.Vec3]]) -> None: 199 """Provide the spaz-bot with the locations of its enemies.""" 200 self._player_pts = pts 201 202 def update_ai(self) -> None: 203 """Should be called periodically to update the spaz' AI.""" 204 # pylint: disable=too-many-branches 205 # pylint: disable=too-many-statements 206 # pylint: disable=too-many-locals 207 if self.update_callback is not None: 208 if self.update_callback(self): 209 # Bot has been handled. 210 return 211 212 if not self.node: 213 return 214 215 pos = self.node.position 216 our_pos = bs.Vec3(pos[0], 0, pos[2]) 217 can_attack = True 218 219 target_pt_raw: bs.Vec3 | None 220 target_vel: bs.Vec3 | None 221 222 # If we're a flag-bearer, we're pretty simple-minded - just walk 223 # towards the flag and try to pick it up. 224 if self.target_flag: 225 if self.node.hold_node: 226 holding_flag = self.node.hold_node.getnodetype() == 'flag' 227 else: 228 holding_flag = False 229 230 # If we're holding the flag, just walk left. 231 if holding_flag: 232 # Just walk left. 233 self.node.move_left_right = -1.0 234 self.node.move_up_down = 0.0 235 236 # Otherwise try to go pick it up. 237 elif self.target_flag.node: 238 target_pt_raw = bs.Vec3(*self.target_flag.node.position) 239 diff = target_pt_raw - our_pos 240 diff = bs.Vec3(diff[0], 0, diff[2]) # Don't care about y. 241 dist = diff.length() 242 to_target = diff.normalized() 243 244 # If we're holding some non-flag item, drop it. 245 if self.node.hold_node: 246 self.node.pickup_pressed = True 247 self.node.pickup_pressed = False 248 return 249 250 # If we're a runner, run only when not super-near the flag. 251 if self.run and dist > 3.0: 252 self._running = True 253 self.node.run = 1.0 254 else: 255 self._running = False 256 self.node.run = 0.0 257 258 self.node.move_left_right = to_target.x 259 self.node.move_up_down = -to_target.z 260 if dist < 1.25: 261 self.node.pickup_pressed = True 262 self.node.pickup_pressed = False 263 return 264 265 # Not a flag-bearer. If we're holding anything but a bomb, drop it. 266 if self.node.hold_node: 267 holding_bomb = self.node.hold_node.getnodetype() in ['bomb', 'prop'] 268 if not holding_bomb: 269 self.node.pickup_pressed = True 270 self.node.pickup_pressed = False 271 return 272 273 target_pt_raw, target_vel = self._get_target_player_pt() 274 275 if target_pt_raw is None: 276 # Use default target if we've got one. 277 if self.target_point_default is not None: 278 target_pt_raw = self.target_point_default 279 target_vel = bs.Vec3(0, 0, 0) 280 can_attack = False 281 282 # With no target, we stop moving and drop whatever we're holding. 283 else: 284 self.node.move_left_right = 0 285 self.node.move_up_down = 0 286 if self.node.hold_node: 287 self.node.pickup_pressed = True 288 self.node.pickup_pressed = False 289 return 290 291 # We don't want height to come into play. 292 target_pt_raw[1] = 0.0 293 assert target_vel is not None 294 target_vel[1] = 0.0 295 296 dist_raw = (target_pt_raw - our_pos).length() 297 298 # Use a point out in front of them as real target. 299 # (more out in front the farther from us they are) 300 target_pt = ( 301 target_pt_raw + target_vel * dist_raw * 0.3 * self._lead_amount 302 ) 303 304 diff = target_pt - our_pos 305 dist = diff.length() 306 to_target = diff.normalized() 307 308 if self._mode == 'throw': 309 # We can only throw if alive and well. 310 if not self._dead and not self.node.knockout: 311 assert self._throw_release_time is not None 312 time_till_throw = self._throw_release_time - bs.time() 313 314 if not self.node.hold_node: 315 # If we haven't thrown yet, whip out the bomb. 316 if not self._have_dropped_throw_bomb: 317 self.drop_bomb() 318 self._have_dropped_throw_bomb = True 319 320 # Otherwise our lack of held node means we successfully 321 # released our bomb; lets retreat now. 322 else: 323 self._mode = 'flee' 324 325 # Oh crap, we're holding a bomb; better throw it. 326 elif time_till_throw <= 0.0: 327 # Jump and throw. 328 def _safe_pickup(node: bs.Node) -> None: 329 if node and self.node: 330 self.node.pickup_pressed = True 331 self.node.pickup_pressed = False 332 333 if dist > 5.0: 334 self.node.jump_pressed = True 335 self.node.jump_pressed = False 336 337 # Throws: 338 bs.timer(0.1, bs.Call(_safe_pickup, self.node)) 339 else: 340 # Throws: 341 bs.timer(0.1, bs.Call(_safe_pickup, self.node)) 342 343 if self.static: 344 if time_till_throw < 0.3: 345 speed = 1.0 346 elif time_till_throw < 0.7 and dist > 3.0: 347 speed = -1.0 # Whiplash for long throws. 348 else: 349 speed = 0.02 350 else: 351 if time_till_throw < 0.7: 352 # Right before throw charge full speed towards target. 353 speed = 1.0 354 else: 355 # Earlier we can hold or move backward for a whiplash. 356 speed = 0.0125 357 self.node.move_left_right = to_target.x * speed 358 self.node.move_up_down = to_target.z * -1.0 * speed 359 360 elif self._mode == 'charge': 361 if random.random() < 0.3: 362 self._charge_speed = random.uniform( 363 self.charge_speed_min, self.charge_speed_max 364 ) 365 366 # If we're a runner we run during charges *except when near 367 # an edge (otherwise we tend to fly off easily). 368 if self.run and dist_raw > self.run_dist_min: 369 self._lead_amount = 0.3 370 self._running = True 371 self.node.run = 1.0 372 else: 373 self._lead_amount = 0.01 374 self._running = False 375 self.node.run = 0.0 376 377 self.node.move_left_right = to_target.x * self._charge_speed 378 self.node.move_up_down = to_target.z * -1.0 * self._charge_speed 379 380 elif self._mode == 'wait': 381 # Every now and then, aim towards our target. 382 # Other than that, just stand there. 383 if int(bs.time() * 1000.0) % 1234 < 100: 384 self.node.move_left_right = to_target.x * (400.0 / 33000) 385 self.node.move_up_down = to_target.z * (-400.0 / 33000) 386 else: 387 self.node.move_left_right = 0 388 self.node.move_up_down = 0 389 390 elif self._mode == 'flee': 391 # Even if we're a runner, only run till we get away from our 392 # target (if we keep running we tend to run off edges). 393 if self.run and dist < 3.0: 394 self._running = True 395 self.node.run = 1.0 396 else: 397 self._running = False 398 self.node.run = 0.0 399 self.node.move_left_right = to_target.x * -1.0 400 self.node.move_up_down = to_target.z 401 402 # We might wanna switch states unless we're doing a throw 403 # (in which case that's our sole concern). 404 if self._mode != 'throw': 405 # If we're currently charging, keep track of how far we are 406 # from our target. When this value increases it means our charge 407 # is over (ran by them or something). 408 if self._mode == 'charge': 409 if ( 410 self._charge_closing_in 411 and self._last_charge_dist < dist < 3.0 412 ): 413 self._charge_closing_in = False 414 self._last_charge_dist = dist 415 416 # If we have a clean shot, throw! 417 if ( 418 self.throw_dist_min <= dist < self.throw_dist_max 419 and random.random() < self.throwiness 420 and can_attack 421 ): 422 self._mode = 'throw' 423 self._lead_amount = ( 424 (0.4 + random.random() * 0.6) 425 if dist_raw > 4.0 426 else (0.1 + random.random() * 0.4) 427 ) 428 self._have_dropped_throw_bomb = False 429 self._throw_release_time = bs.time() + ( 430 1.0 / self.throw_rate 431 ) * (0.8 + 1.3 * random.random()) 432 433 # If we're static, always charge (which for us means barely move). 434 elif self.static: 435 self._mode = 'wait' 436 437 # If we're too close to charge (and aren't in the middle of an 438 # existing charge) run away. 439 elif dist < self.charge_dist_min and not self._charge_closing_in: 440 # ..unless we're near an edge, in which case we've got no 441 # choice but to charge. 442 if self.map.is_point_near_edge(our_pos, self._running): 443 if self._mode != 'charge': 444 self._mode = 'charge' 445 self._lead_amount = 0.2 446 self._charge_closing_in = True 447 self._last_charge_dist = dist 448 else: 449 self._mode = 'flee' 450 451 # We're within charging distance, backed against an edge, 452 # or farther than our max throw distance.. chaaarge! 453 elif ( 454 dist < self.charge_dist_max 455 or dist > self.throw_dist_max 456 or self.map.is_point_near_edge(our_pos, self._running) 457 ): 458 if self._mode != 'charge': 459 self._mode = 'charge' 460 self._lead_amount = 0.01 461 self._charge_closing_in = True 462 self._last_charge_dist = dist 463 464 # We're too close to throw but too far to charge - either run 465 # away or just chill if we're near an edge. 466 elif dist < self.throw_dist_min: 467 # Charge if either we're within charge range or 468 # cant retreat to throw. 469 self._mode = 'flee' 470 471 # Do some awesome jumps if we're running. 472 # FIXME: pylint: disable=too-many-boolean-expressions 473 if ( 474 self._running 475 and 1.2 < dist < 2.2 476 and bs.time() - self._last_jump_time > 1.0 477 ) or ( 478 self.bouncy 479 and bs.time() - self._last_jump_time > 0.4 480 and random.random() < 0.5 481 ): 482 self._last_jump_time = bs.time() 483 self.node.jump_pressed = True 484 self.node.jump_pressed = False 485 486 # Throw punches when real close. 487 if dist < (1.6 if self._running else 1.2) and can_attack: 488 if random.random() < self.punchiness: 489 self.on_punch_press() 490 self.on_punch_release() 491 492 def on_punched(self, damage: int) -> None: 493 """ 494 Method override; sends bs.SpazBotPunchedMessage 495 to the current activity. 496 """ 497 bs.getactivity().handlemessage(SpazBotPunchedMessage(self, damage)) 498 499 def on_expire(self) -> None: 500 super().on_expire() 501 502 # We're being torn down; release our callback(s) so there's 503 # no chance of them keeping activities or other things alive. 504 self.update_callback = None 505 506 def handlemessage(self, msg: Any) -> Any: 507 # pylint: disable=too-many-branches 508 assert not self.expired 509 510 # Keep track of if we're being held and by who most recently. 511 if isinstance(msg, bs.PickedUpMessage): 512 super().handlemessage(msg) # Augment standard behavior. 513 self.held_count += 1 514 picked_up_by = msg.node.source_player 515 if picked_up_by: 516 self.last_player_held_by = picked_up_by 517 518 elif isinstance(msg, bs.DroppedMessage): 519 super().handlemessage(msg) # Augment standard behavior. 520 self.held_count -= 1 521 if self.held_count < 0: 522 print('ERROR: spaz held_count < 0') 523 524 # Let's count someone dropping us as an attack. 525 try: 526 if msg.node: 527 picked_up_by = msg.node.source_player 528 else: 529 picked_up_by = None 530 except Exception: 531 logging.exception('Error on SpazBot DroppedMessage.') 532 picked_up_by = None 533 534 if picked_up_by: 535 self.last_player_attacked_by = picked_up_by 536 self.last_attacked_time = bs.time() 537 self.last_attacked_type = ('picked_up', 'default') 538 539 elif isinstance(msg, bs.DieMessage): 540 # Report normal deaths for scoring purposes. 541 if not self._dead and not msg.immediate: 542 killerplayer: bs.Player | None 543 544 # If this guy was being held at the time of death, the 545 # holder is the killer. 546 if self.held_count > 0 and self.last_player_held_by: 547 killerplayer = self.last_player_held_by 548 else: 549 # If they were attacked by someone in the last few 550 # seconds that person's the killer. 551 # Otherwise it was a suicide. 552 if ( 553 self.last_player_attacked_by 554 and bs.time() - self.last_attacked_time < 4.0 555 ): 556 killerplayer = self.last_player_attacked_by 557 else: 558 killerplayer = None 559 activity = self._activity() 560 561 # (convert dead player refs to None) 562 if not killerplayer: 563 killerplayer = None 564 if activity is not None: 565 activity.handlemessage( 566 SpazBotDiedMessage(self, killerplayer, msg.how) 567 ) 568 super().handlemessage(msg) # Augment standard behavior. 569 570 # Keep track of the player who last hit us for point rewarding. 571 elif isinstance(msg, bs.HitMessage): 572 source_player = msg.get_source_player(bs.Player) 573 if source_player: 574 self.last_player_attacked_by = source_player 575 self.last_attacked_time = bs.time() 576 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 577 super().handlemessage(msg) 578 else: 579 super().handlemessage(msg) 580 581 582class BomberBot(SpazBot): 583 """A bot that throws regular bombs and occasionally punches. 584 585 category: Bot Classes 586 """ 587 588 character = 'Spaz' 589 punchiness = 0.3 590 591 592class BomberBotLite(BomberBot): 593 """A less aggressive yellow version of bs.BomberBot. 594 595 category: Bot Classes 596 """ 597 598 color = LITE_BOT_COLOR 599 highlight = LITE_BOT_HIGHLIGHT 600 punchiness = 0.2 601 throw_rate = 0.7 602 throwiness = 0.1 603 charge_speed_min = 0.6 604 charge_speed_max = 0.6 605 606 607class BomberBotStaticLite(BomberBotLite): 608 """A less aggressive generally immobile weak version of bs.BomberBot. 609 610 category: Bot Classes 611 """ 612 613 static = True 614 throw_dist_min = 0.0 615 616 617class BomberBotStatic(BomberBot): 618 """A version of bs.BomberBot who generally stays in one place. 619 620 category: Bot Classes 621 """ 622 623 static = True 624 throw_dist_min = 0.0 625 626 627class BomberBotPro(BomberBot): 628 """A more powerful version of bs.BomberBot. 629 630 category: Bot Classes 631 """ 632 633 points_mult = 2 634 color = PRO_BOT_COLOR 635 highlight = PRO_BOT_HIGHLIGHT 636 default_bomb_count = 3 637 default_boxing_gloves = True 638 punchiness = 0.7 639 throw_rate = 1.3 640 run = True 641 run_dist_min = 6.0 642 643 644class BomberBotProShielded(BomberBotPro): 645 """A more powerful version of bs.BomberBot who starts with shields. 646 647 category: Bot Classes 648 """ 649 650 points_mult = 3 651 default_shields = True 652 653 654class BomberBotProStatic(BomberBotPro): 655 """A more powerful bs.BomberBot who generally stays in one place. 656 657 category: Bot Classes 658 """ 659 660 static = True 661 throw_dist_min = 0.0 662 663 664class BomberBotProStaticShielded(BomberBotProShielded): 665 """A powerful bs.BomberBot with shields who is generally immobile. 666 667 category: Bot Classes 668 """ 669 670 static = True 671 throw_dist_min = 0.0 672 673 674class BrawlerBot(SpazBot): 675 """A bot who walks and punches things. 676 677 category: Bot Classes 678 """ 679 680 character = 'Kronk' 681 punchiness = 0.9 682 charge_dist_max = 9999.0 683 charge_speed_min = 1.0 684 charge_speed_max = 1.0 685 throw_dist_min = 9999 686 throw_dist_max = 9999 687 688 689class BrawlerBotLite(BrawlerBot): 690 """A weaker version of bs.BrawlerBot. 691 692 category: Bot Classes 693 """ 694 695 color = LITE_BOT_COLOR 696 highlight = LITE_BOT_HIGHLIGHT 697 punchiness = 0.3 698 charge_speed_min = 0.6 699 charge_speed_max = 0.6 700 701 702class BrawlerBotPro(BrawlerBot): 703 """A stronger version of bs.BrawlerBot. 704 705 category: Bot Classes 706 """ 707 708 color = PRO_BOT_COLOR 709 highlight = PRO_BOT_HIGHLIGHT 710 run = True 711 run_dist_min = 4.0 712 default_boxing_gloves = True 713 punchiness = 0.95 714 points_mult = 2 715 716 717class BrawlerBotProShielded(BrawlerBotPro): 718 """A stronger version of bs.BrawlerBot who starts with shields. 719 720 category: Bot Classes 721 """ 722 723 default_shields = True 724 points_mult = 3 725 726 727class ChargerBot(SpazBot): 728 """A speedy melee attack bot. 729 730 category: Bot Classes 731 """ 732 733 character = 'Snake Shadow' 734 punchiness = 1.0 735 run = True 736 charge_dist_min = 10.0 737 charge_dist_max = 9999.0 738 charge_speed_min = 1.0 739 charge_speed_max = 1.0 740 throw_dist_min = 9999 741 throw_dist_max = 9999 742 points_mult = 2 743 744 745class BouncyBot(SpazBot): 746 """A speedy attacking melee bot that jumps constantly. 747 748 category: Bot Classes 749 """ 750 751 color = (1, 1, 1) 752 highlight = (1.0, 0.5, 0.5) 753 character = 'Easter Bunny' 754 punchiness = 1.0 755 run = True 756 bouncy = True 757 default_boxing_gloves = True 758 charge_dist_min = 10.0 759 charge_dist_max = 9999.0 760 charge_speed_min = 1.0 761 charge_speed_max = 1.0 762 throw_dist_min = 9999 763 throw_dist_max = 9999 764 points_mult = 2 765 766 767class ChargerBotPro(ChargerBot): 768 """A stronger bs.ChargerBot. 769 770 category: Bot Classes 771 """ 772 773 color = PRO_BOT_COLOR 774 highlight = PRO_BOT_HIGHLIGHT 775 default_shields = True 776 default_boxing_gloves = True 777 points_mult = 3 778 779 780class ChargerBotProShielded(ChargerBotPro): 781 """A stronger bs.ChargerBot who starts with shields. 782 783 category: Bot Classes 784 """ 785 786 default_shields = True 787 points_mult = 4 788 789 790class TriggerBot(SpazBot): 791 """A slow moving bot with trigger bombs. 792 793 category: Bot Classes 794 """ 795 796 character = 'Zoe' 797 punchiness = 0.75 798 throwiness = 0.7 799 charge_dist_max = 1.0 800 charge_speed_min = 0.3 801 charge_speed_max = 0.5 802 throw_dist_min = 3.5 803 throw_dist_max = 5.5 804 default_bomb_type = 'impact' 805 points_mult = 2 806 807 808class TriggerBotStatic(TriggerBot): 809 """A bs.TriggerBot who generally stays in one place. 810 811 category: Bot Classes 812 """ 813 814 static = True 815 throw_dist_min = 0.0 816 817 818class TriggerBotPro(TriggerBot): 819 """A stronger version of bs.TriggerBot. 820 821 category: Bot Classes 822 """ 823 824 color = PRO_BOT_COLOR 825 highlight = PRO_BOT_HIGHLIGHT 826 default_bomb_count = 3 827 default_boxing_gloves = True 828 charge_speed_min = 1.0 829 charge_speed_max = 1.0 830 punchiness = 0.9 831 throw_rate = 1.3 832 run = True 833 run_dist_min = 6.0 834 points_mult = 3 835 836 837class TriggerBotProShielded(TriggerBotPro): 838 """A stronger version of bs.TriggerBot who starts with shields. 839 840 category: Bot Classes 841 """ 842 843 default_shields = True 844 points_mult = 4 845 846 847class StickyBot(SpazBot): 848 """A crazy bot who runs and throws sticky bombs. 849 850 category: Bot Classes 851 """ 852 853 character = 'Mel' 854 punchiness = 0.9 855 throwiness = 1.0 856 run = True 857 charge_dist_min = 4.0 858 charge_dist_max = 10.0 859 charge_speed_min = 1.0 860 charge_speed_max = 1.0 861 throw_dist_min = 0.0 862 throw_dist_max = 4.0 863 throw_rate = 2.0 864 default_bomb_type = 'sticky' 865 default_bomb_count = 3 866 points_mult = 3 867 868 869class StickyBotStatic(StickyBot): 870 """A crazy bot who throws sticky-bombs but generally stays in one place. 871 872 category: Bot Classes 873 """ 874 875 static = True 876 877 878class ExplodeyBot(SpazBot): 879 """A bot who runs and explodes in 5 seconds. 880 881 category: Bot Classes 882 """ 883 884 character = 'Jack Morgan' 885 run = True 886 charge_dist_min = 0.0 887 charge_dist_max = 9999 888 charge_speed_min = 1.0 889 charge_speed_max = 1.0 890 throw_dist_min = 9999 891 throw_dist_max = 9999 892 start_cursed = True 893 points_mult = 4 894 895 896class ExplodeyBotNoTimeLimit(ExplodeyBot): 897 """A bot who runs but does not explode on his own. 898 899 category: Bot Classes 900 """ 901 902 curse_time = None 903 904 905class ExplodeyBotShielded(ExplodeyBot): 906 """A bs.ExplodeyBot who starts with shields. 907 908 category: Bot Classes 909 """ 910 911 default_shields = True 912 points_mult = 5 913 914 915class SpazBotSet: 916 """A container/controller for one or more bs.SpazBots. 917 918 category: Bot Classes 919 """ 920 921 def __init__(self) -> None: 922 """Create a bot-set.""" 923 924 # We spread our bots out over a few lists so we can update 925 # them in a staggered fashion. 926 self._bot_list_count = 5 927 self._bot_add_list = 0 928 self._bot_update_list = 0 929 self._bot_lists: list[list[SpazBot]] = [ 930 [] for _ in range(self._bot_list_count) 931 ] 932 self._spawn_sound = bs.getsound('spawn') 933 self._spawning_count = 0 934 self._bot_update_timer: bs.Timer | None = None 935 self.start_moving() 936 937 def __del__(self) -> None: 938 self.clear() 939 940 def spawn_bot( 941 self, 942 bot_type: type[SpazBot], 943 pos: Sequence[float], 944 spawn_time: float = 3.0, 945 on_spawn_call: Callable[[SpazBot], Any] | None = None, 946 ) -> None: 947 """Spawn a bot from this set.""" 948 from bascenev1lib.actor import spawner 949 950 spawner.Spawner( 951 pt=pos, 952 spawn_time=spawn_time, 953 send_spawn_message=False, 954 spawn_callback=bs.Call( 955 self._spawn_bot, bot_type, pos, on_spawn_call 956 ), 957 ) 958 self._spawning_count += 1 959 960 def _spawn_bot( 961 self, 962 bot_type: type[SpazBot], 963 pos: Sequence[float], 964 on_spawn_call: Callable[[SpazBot], Any] | None, 965 ) -> None: 966 spaz = bot_type() 967 self._spawn_sound.play(position=pos) 968 assert spaz.node 969 spaz.node.handlemessage('flash') 970 spaz.node.is_area_of_interest = False 971 spaz.handlemessage(bs.StandMessage(pos, random.uniform(0, 360))) 972 self.add_bot(spaz) 973 self._spawning_count -= 1 974 if on_spawn_call is not None: 975 on_spawn_call(spaz) 976 977 def have_living_bots(self) -> bool: 978 """Return whether any bots in the set are alive or spawning.""" 979 return self._spawning_count > 0 or any( 980 any(b.is_alive() for b in l) for l in self._bot_lists 981 ) 982 983 def get_living_bots(self) -> list[SpazBot]: 984 """Get the living bots in the set.""" 985 bots: list[SpazBot] = [] 986 for botlist in self._bot_lists: 987 for bot in botlist: 988 if bot.is_alive(): 989 bots.append(bot) 990 return bots 991 992 def _update(self) -> None: 993 # Update one of our bot lists each time through. 994 # First off, remove no-longer-existing bots from the list. 995 try: 996 bot_list = self._bot_lists[self._bot_update_list] = [ 997 b for b in self._bot_lists[self._bot_update_list] if b 998 ] 999 except Exception: 1000 bot_list = [] 1001 logging.exception( 1002 'Error updating bot list: %s', 1003 self._bot_lists[self._bot_update_list], 1004 ) 1005 self._bot_update_list = ( 1006 self._bot_update_list + 1 1007 ) % self._bot_list_count 1008 1009 # Update our list of player points for the bots to use. 1010 player_pts = [] 1011 for player in bs.getactivity().players: 1012 assert isinstance(player, bs.Player) 1013 try: 1014 # TODO: could use abstracted player.position here so we 1015 # don't have to assume their actor type, but we have no 1016 # abstracted velocity as of yet. 1017 if player.is_alive(): 1018 assert isinstance(player.actor, Spaz) 1019 assert player.actor.node 1020 player_pts.append( 1021 ( 1022 bs.Vec3(player.actor.node.position), 1023 bs.Vec3(player.actor.node.velocity), 1024 ) 1025 ) 1026 except Exception: 1027 logging.exception('Error on bot-set _update.') 1028 1029 for bot in bot_list: 1030 bot.set_player_points(player_pts) 1031 bot.update_ai() 1032 1033 def clear(self) -> None: 1034 """Immediately clear out any bots in the set.""" 1035 1036 # Don't do this if the activity is shutting down or dead. 1037 activity = bs.getactivity(doraise=False) 1038 if activity is None or activity.expired: 1039 return 1040 1041 for i, bot_list in enumerate(self._bot_lists): 1042 for bot in bot_list: 1043 bot.handlemessage(bs.DieMessage(immediate=True)) 1044 self._bot_lists[i] = [] 1045 1046 def start_moving(self) -> None: 1047 """Start processing bot AI updates so they start doing their thing.""" 1048 self._bot_update_timer = bs.Timer( 1049 0.05, bs.WeakCall(self._update), repeat=True 1050 ) 1051 1052 def stop_moving(self) -> None: 1053 """Tell all bots to stop moving and stops updating their AI. 1054 1055 Useful when players have won and you want the 1056 enemy bots to just stand and look bewildered. 1057 """ 1058 self._bot_update_timer = None 1059 for botlist in self._bot_lists: 1060 for bot in botlist: 1061 if bot.node: 1062 bot.node.move_left_right = 0 1063 bot.node.move_up_down = 0 1064 1065 def celebrate(self, duration: float) -> None: 1066 """Tell all living bots in the set to celebrate momentarily. 1067 1068 Duration is given in seconds. 1069 """ 1070 msg = bs.CelebrateMessage(duration=duration) 1071 for botlist in self._bot_lists: 1072 for bot in botlist: 1073 if bot: 1074 bot.handlemessage(msg) 1075 1076 def final_celebrate(self) -> None: 1077 """Tell all bots in the set to stop what they were doing and celebrate. 1078 1079 Use this when the bots have won a game. 1080 """ 1081 self._bot_update_timer = None 1082 1083 # At this point stop doing anything but jumping and celebrating. 1084 for botlist in self._bot_lists: 1085 for bot in botlist: 1086 if bot: 1087 assert bot.node # (should exist if 'if bot' was True) 1088 bot.node.move_left_right = 0 1089 bot.node.move_up_down = 0 1090 bs.timer( 1091 0.5 * random.random(), 1092 bs.Call(bot.handlemessage, bs.CelebrateMessage()), 1093 ) 1094 jump_duration = random.randrange(400, 500) 1095 j = random.randrange(0, 200) 1096 for _i in range(10): 1097 bot.node.jump_pressed = True 1098 bot.node.jump_pressed = False 1099 j += jump_duration 1100 bs.timer( 1101 random.uniform(0.0, 1.0), 1102 bs.Call(bot.node.handlemessage, 'attack_sound'), 1103 ) 1104 bs.timer( 1105 random.uniform(1.0, 2.0), 1106 bs.Call(bot.node.handlemessage, 'attack_sound'), 1107 ) 1108 bs.timer( 1109 random.uniform(2.0, 3.0), 1110 bs.Call(bot.node.handlemessage, 'attack_sound'), 1111 ) 1112 1113 def add_bot(self, bot: SpazBot) -> None: 1114 """Add a bs.SpazBot instance to the set.""" 1115 self._bot_lists[self._bot_add_list].append(bot) 1116 self._bot_add_list = (self._bot_add_list + 1) % self._bot_list_count
29class SpazBotPunchedMessage: 30 """A message saying a bs.SpazBot got punched. 31 32 Category: **Message Classes** 33 """ 34 35 spazbot: SpazBot 36 """The bs.SpazBot that got punched.""" 37 38 damage: int 39 """How much damage was done to the SpazBot.""" 40 41 def __init__(self, spazbot: SpazBot, damage: int): 42 """Instantiate a message with the given values.""" 43 self.spazbot = spazbot 44 self.damage = damage
A message saying a bs.SpazBot got punched.
Category: Message Classes
47class SpazBotDiedMessage: 48 """A message saying a bs.SpazBot has died. 49 50 Category: **Message Classes** 51 """ 52 53 spazbot: SpazBot 54 """The SpazBot that was killed.""" 55 56 killerplayer: bs.Player | None 57 """The bascenev1.Player that killed it (or None).""" 58 59 how: bs.DeathType 60 """The particular type of death.""" 61 62 def __init__( 63 self, 64 spazbot: SpazBot, 65 killerplayer: bs.Player | None, 66 how: bs.DeathType, 67 ): 68 """Instantiate with given values.""" 69 self.spazbot = spazbot 70 self.killerplayer = killerplayer 71 self.how = how
A message saying a bs.SpazBot has died.
Category: Message Classes
62 def __init__( 63 self, 64 spazbot: SpazBot, 65 killerplayer: bs.Player | None, 66 how: bs.DeathType, 67 ): 68 """Instantiate with given values.""" 69 self.spazbot = spazbot 70 self.killerplayer = killerplayer 71 self.how = how
Instantiate with given values.
74class SpazBot(Spaz): 75 """A really dumb AI version of bs.Spaz. 76 77 Category: **Bot Classes** 78 79 Add these to a bs.BotSet to use them. 80 81 Note: currently the AI has no real ability to 82 navigate obstacles and so should only be used 83 on wide-open maps. 84 85 When a SpazBot is killed, it delivers a bs.SpazBotDiedMessage 86 to the current activity. 87 88 When a SpazBot is punched, it delivers a bs.SpazBotPunchedMessage 89 to the current activity. 90 """ 91 92 character = 'Spaz' 93 punchiness = 0.5 94 throwiness = 0.7 95 static = False 96 bouncy = False 97 run = False 98 charge_dist_min = 0.0 # When we can start a new charge. 99 charge_dist_max = 2.0 # When we can start a new charge. 100 run_dist_min = 0.0 # How close we can be to continue running. 101 charge_speed_min = 0.4 102 charge_speed_max = 1.0 103 throw_dist_min = 5.0 104 throw_dist_max = 9.0 105 throw_rate = 1.0 106 default_bomb_type = 'normal' 107 default_bomb_count = 3 108 start_cursed = False 109 color = DEFAULT_BOT_COLOR 110 highlight = DEFAULT_BOT_HIGHLIGHT 111 112 def __init__(self) -> None: 113 """Instantiate a spaz-bot.""" 114 super().__init__( 115 color=self.color, 116 highlight=self.highlight, 117 character=self.character, 118 source_player=None, 119 start_invincible=False, 120 can_accept_powerups=False, 121 ) 122 123 # If you need to add custom behavior to a bot, set this to a callable 124 # which takes one arg (the bot) and returns False if the bot's normal 125 # update should be run and True if not. 126 self.update_callback: Callable[[SpazBot], Any] | None = None 127 activity = self.activity 128 assert isinstance(activity, bs.GameActivity) 129 self._map = weakref.ref(activity.map) 130 self.last_player_attacked_by: bs.Player | None = None 131 self.last_attacked_time = 0.0 132 self.last_attacked_type: tuple[str, str] | None = None 133 self.target_point_default: bs.Vec3 | None = None 134 self.held_count = 0 135 self.last_player_held_by: bs.Player | None = None 136 self.target_flag: Flag | None = None 137 self._charge_speed = 0.5 * ( 138 self.charge_speed_min + self.charge_speed_max 139 ) 140 self._lead_amount = 0.5 141 self._mode = 'wait' 142 self._charge_closing_in = False 143 self._last_charge_dist = 0.0 144 self._running = False 145 self._last_jump_time = 0.0 146 147 self._throw_release_time: float | None = None 148 self._have_dropped_throw_bomb: bool | None = None 149 self._player_pts: list[tuple[bs.Vec3, bs.Vec3]] | None = None 150 151 # These cooldowns didn't exist when these bots were calibrated, 152 # so take them out of the equation. 153 self._jump_cooldown = 0 154 self._pickup_cooldown = 0 155 self._fly_cooldown = 0 156 self._bomb_cooldown = 0 157 158 if self.start_cursed: 159 self.curse() 160 161 @property 162 def map(self) -> bs.Map: 163 """The map this bot was created on.""" 164 mval = self._map() 165 assert mval is not None 166 return mval 167 168 def _get_target_player_pt(self) -> tuple[bs.Vec3 | None, bs.Vec3 | None]: 169 """Returns the position and velocity of our target. 170 171 Both values will be None in the case of no target. 172 """ 173 assert self.node 174 botpt = bs.Vec3(self.node.position) 175 closest_dist: float | None = None 176 closest_vel: bs.Vec3 | None = None 177 closest: bs.Vec3 | None = None 178 assert self._player_pts is not None 179 for plpt, plvel in self._player_pts: 180 dist = (plpt - botpt).length() 181 182 # Ignore player-points that are significantly below the bot 183 # (keeps bots from following players off cliffs). 184 if (closest_dist is None or dist < closest_dist) and ( 185 plpt[1] > botpt[1] - 5.0 186 ): 187 closest_dist = dist 188 closest_vel = plvel 189 closest = plpt 190 if closest_dist is not None: 191 assert closest_vel is not None 192 assert closest is not None 193 return ( 194 bs.Vec3(closest[0], closest[1], closest[2]), 195 bs.Vec3(closest_vel[0], closest_vel[1], closest_vel[2]), 196 ) 197 return None, None 198 199 def set_player_points(self, pts: list[tuple[bs.Vec3, bs.Vec3]]) -> None: 200 """Provide the spaz-bot with the locations of its enemies.""" 201 self._player_pts = pts 202 203 def update_ai(self) -> None: 204 """Should be called periodically to update the spaz' AI.""" 205 # pylint: disable=too-many-branches 206 # pylint: disable=too-many-statements 207 # pylint: disable=too-many-locals 208 if self.update_callback is not None: 209 if self.update_callback(self): 210 # Bot has been handled. 211 return 212 213 if not self.node: 214 return 215 216 pos = self.node.position 217 our_pos = bs.Vec3(pos[0], 0, pos[2]) 218 can_attack = True 219 220 target_pt_raw: bs.Vec3 | None 221 target_vel: bs.Vec3 | None 222 223 # If we're a flag-bearer, we're pretty simple-minded - just walk 224 # towards the flag and try to pick it up. 225 if self.target_flag: 226 if self.node.hold_node: 227 holding_flag = self.node.hold_node.getnodetype() == 'flag' 228 else: 229 holding_flag = False 230 231 # If we're holding the flag, just walk left. 232 if holding_flag: 233 # Just walk left. 234 self.node.move_left_right = -1.0 235 self.node.move_up_down = 0.0 236 237 # Otherwise try to go pick it up. 238 elif self.target_flag.node: 239 target_pt_raw = bs.Vec3(*self.target_flag.node.position) 240 diff = target_pt_raw - our_pos 241 diff = bs.Vec3(diff[0], 0, diff[2]) # Don't care about y. 242 dist = diff.length() 243 to_target = diff.normalized() 244 245 # If we're holding some non-flag item, drop it. 246 if self.node.hold_node: 247 self.node.pickup_pressed = True 248 self.node.pickup_pressed = False 249 return 250 251 # If we're a runner, run only when not super-near the flag. 252 if self.run and dist > 3.0: 253 self._running = True 254 self.node.run = 1.0 255 else: 256 self._running = False 257 self.node.run = 0.0 258 259 self.node.move_left_right = to_target.x 260 self.node.move_up_down = -to_target.z 261 if dist < 1.25: 262 self.node.pickup_pressed = True 263 self.node.pickup_pressed = False 264 return 265 266 # Not a flag-bearer. If we're holding anything but a bomb, drop it. 267 if self.node.hold_node: 268 holding_bomb = self.node.hold_node.getnodetype() in ['bomb', 'prop'] 269 if not holding_bomb: 270 self.node.pickup_pressed = True 271 self.node.pickup_pressed = False 272 return 273 274 target_pt_raw, target_vel = self._get_target_player_pt() 275 276 if target_pt_raw is None: 277 # Use default target if we've got one. 278 if self.target_point_default is not None: 279 target_pt_raw = self.target_point_default 280 target_vel = bs.Vec3(0, 0, 0) 281 can_attack = False 282 283 # With no target, we stop moving and drop whatever we're holding. 284 else: 285 self.node.move_left_right = 0 286 self.node.move_up_down = 0 287 if self.node.hold_node: 288 self.node.pickup_pressed = True 289 self.node.pickup_pressed = False 290 return 291 292 # We don't want height to come into play. 293 target_pt_raw[1] = 0.0 294 assert target_vel is not None 295 target_vel[1] = 0.0 296 297 dist_raw = (target_pt_raw - our_pos).length() 298 299 # Use a point out in front of them as real target. 300 # (more out in front the farther from us they are) 301 target_pt = ( 302 target_pt_raw + target_vel * dist_raw * 0.3 * self._lead_amount 303 ) 304 305 diff = target_pt - our_pos 306 dist = diff.length() 307 to_target = diff.normalized() 308 309 if self._mode == 'throw': 310 # We can only throw if alive and well. 311 if not self._dead and not self.node.knockout: 312 assert self._throw_release_time is not None 313 time_till_throw = self._throw_release_time - bs.time() 314 315 if not self.node.hold_node: 316 # If we haven't thrown yet, whip out the bomb. 317 if not self._have_dropped_throw_bomb: 318 self.drop_bomb() 319 self._have_dropped_throw_bomb = True 320 321 # Otherwise our lack of held node means we successfully 322 # released our bomb; lets retreat now. 323 else: 324 self._mode = 'flee' 325 326 # Oh crap, we're holding a bomb; better throw it. 327 elif time_till_throw <= 0.0: 328 # Jump and throw. 329 def _safe_pickup(node: bs.Node) -> None: 330 if node and self.node: 331 self.node.pickup_pressed = True 332 self.node.pickup_pressed = False 333 334 if dist > 5.0: 335 self.node.jump_pressed = True 336 self.node.jump_pressed = False 337 338 # Throws: 339 bs.timer(0.1, bs.Call(_safe_pickup, self.node)) 340 else: 341 # Throws: 342 bs.timer(0.1, bs.Call(_safe_pickup, self.node)) 343 344 if self.static: 345 if time_till_throw < 0.3: 346 speed = 1.0 347 elif time_till_throw < 0.7 and dist > 3.0: 348 speed = -1.0 # Whiplash for long throws. 349 else: 350 speed = 0.02 351 else: 352 if time_till_throw < 0.7: 353 # Right before throw charge full speed towards target. 354 speed = 1.0 355 else: 356 # Earlier we can hold or move backward for a whiplash. 357 speed = 0.0125 358 self.node.move_left_right = to_target.x * speed 359 self.node.move_up_down = to_target.z * -1.0 * speed 360 361 elif self._mode == 'charge': 362 if random.random() < 0.3: 363 self._charge_speed = random.uniform( 364 self.charge_speed_min, self.charge_speed_max 365 ) 366 367 # If we're a runner we run during charges *except when near 368 # an edge (otherwise we tend to fly off easily). 369 if self.run and dist_raw > self.run_dist_min: 370 self._lead_amount = 0.3 371 self._running = True 372 self.node.run = 1.0 373 else: 374 self._lead_amount = 0.01 375 self._running = False 376 self.node.run = 0.0 377 378 self.node.move_left_right = to_target.x * self._charge_speed 379 self.node.move_up_down = to_target.z * -1.0 * self._charge_speed 380 381 elif self._mode == 'wait': 382 # Every now and then, aim towards our target. 383 # Other than that, just stand there. 384 if int(bs.time() * 1000.0) % 1234 < 100: 385 self.node.move_left_right = to_target.x * (400.0 / 33000) 386 self.node.move_up_down = to_target.z * (-400.0 / 33000) 387 else: 388 self.node.move_left_right = 0 389 self.node.move_up_down = 0 390 391 elif self._mode == 'flee': 392 # Even if we're a runner, only run till we get away from our 393 # target (if we keep running we tend to run off edges). 394 if self.run and dist < 3.0: 395 self._running = True 396 self.node.run = 1.0 397 else: 398 self._running = False 399 self.node.run = 0.0 400 self.node.move_left_right = to_target.x * -1.0 401 self.node.move_up_down = to_target.z 402 403 # We might wanna switch states unless we're doing a throw 404 # (in which case that's our sole concern). 405 if self._mode != 'throw': 406 # If we're currently charging, keep track of how far we are 407 # from our target. When this value increases it means our charge 408 # is over (ran by them or something). 409 if self._mode == 'charge': 410 if ( 411 self._charge_closing_in 412 and self._last_charge_dist < dist < 3.0 413 ): 414 self._charge_closing_in = False 415 self._last_charge_dist = dist 416 417 # If we have a clean shot, throw! 418 if ( 419 self.throw_dist_min <= dist < self.throw_dist_max 420 and random.random() < self.throwiness 421 and can_attack 422 ): 423 self._mode = 'throw' 424 self._lead_amount = ( 425 (0.4 + random.random() * 0.6) 426 if dist_raw > 4.0 427 else (0.1 + random.random() * 0.4) 428 ) 429 self._have_dropped_throw_bomb = False 430 self._throw_release_time = bs.time() + ( 431 1.0 / self.throw_rate 432 ) * (0.8 + 1.3 * random.random()) 433 434 # If we're static, always charge (which for us means barely move). 435 elif self.static: 436 self._mode = 'wait' 437 438 # If we're too close to charge (and aren't in the middle of an 439 # existing charge) run away. 440 elif dist < self.charge_dist_min and not self._charge_closing_in: 441 # ..unless we're near an edge, in which case we've got no 442 # choice but to charge. 443 if self.map.is_point_near_edge(our_pos, self._running): 444 if self._mode != 'charge': 445 self._mode = 'charge' 446 self._lead_amount = 0.2 447 self._charge_closing_in = True 448 self._last_charge_dist = dist 449 else: 450 self._mode = 'flee' 451 452 # We're within charging distance, backed against an edge, 453 # or farther than our max throw distance.. chaaarge! 454 elif ( 455 dist < self.charge_dist_max 456 or dist > self.throw_dist_max 457 or self.map.is_point_near_edge(our_pos, self._running) 458 ): 459 if self._mode != 'charge': 460 self._mode = 'charge' 461 self._lead_amount = 0.01 462 self._charge_closing_in = True 463 self._last_charge_dist = dist 464 465 # We're too close to throw but too far to charge - either run 466 # away or just chill if we're near an edge. 467 elif dist < self.throw_dist_min: 468 # Charge if either we're within charge range or 469 # cant retreat to throw. 470 self._mode = 'flee' 471 472 # Do some awesome jumps if we're running. 473 # FIXME: pylint: disable=too-many-boolean-expressions 474 if ( 475 self._running 476 and 1.2 < dist < 2.2 477 and bs.time() - self._last_jump_time > 1.0 478 ) or ( 479 self.bouncy 480 and bs.time() - self._last_jump_time > 0.4 481 and random.random() < 0.5 482 ): 483 self._last_jump_time = bs.time() 484 self.node.jump_pressed = True 485 self.node.jump_pressed = False 486 487 # Throw punches when real close. 488 if dist < (1.6 if self._running else 1.2) and can_attack: 489 if random.random() < self.punchiness: 490 self.on_punch_press() 491 self.on_punch_release() 492 493 def on_punched(self, damage: int) -> None: 494 """ 495 Method override; sends bs.SpazBotPunchedMessage 496 to the current activity. 497 """ 498 bs.getactivity().handlemessage(SpazBotPunchedMessage(self, damage)) 499 500 def on_expire(self) -> None: 501 super().on_expire() 502 503 # We're being torn down; release our callback(s) so there's 504 # no chance of them keeping activities or other things alive. 505 self.update_callback = None 506 507 def handlemessage(self, msg: Any) -> Any: 508 # pylint: disable=too-many-branches 509 assert not self.expired 510 511 # Keep track of if we're being held and by who most recently. 512 if isinstance(msg, bs.PickedUpMessage): 513 super().handlemessage(msg) # Augment standard behavior. 514 self.held_count += 1 515 picked_up_by = msg.node.source_player 516 if picked_up_by: 517 self.last_player_held_by = picked_up_by 518 519 elif isinstance(msg, bs.DroppedMessage): 520 super().handlemessage(msg) # Augment standard behavior. 521 self.held_count -= 1 522 if self.held_count < 0: 523 print('ERROR: spaz held_count < 0') 524 525 # Let's count someone dropping us as an attack. 526 try: 527 if msg.node: 528 picked_up_by = msg.node.source_player 529 else: 530 picked_up_by = None 531 except Exception: 532 logging.exception('Error on SpazBot DroppedMessage.') 533 picked_up_by = None 534 535 if picked_up_by: 536 self.last_player_attacked_by = picked_up_by 537 self.last_attacked_time = bs.time() 538 self.last_attacked_type = ('picked_up', 'default') 539 540 elif isinstance(msg, bs.DieMessage): 541 # Report normal deaths for scoring purposes. 542 if not self._dead and not msg.immediate: 543 killerplayer: bs.Player | None 544 545 # If this guy was being held at the time of death, the 546 # holder is the killer. 547 if self.held_count > 0 and self.last_player_held_by: 548 killerplayer = self.last_player_held_by 549 else: 550 # If they were attacked by someone in the last few 551 # seconds that person's the killer. 552 # Otherwise it was a suicide. 553 if ( 554 self.last_player_attacked_by 555 and bs.time() - self.last_attacked_time < 4.0 556 ): 557 killerplayer = self.last_player_attacked_by 558 else: 559 killerplayer = None 560 activity = self._activity() 561 562 # (convert dead player refs to None) 563 if not killerplayer: 564 killerplayer = None 565 if activity is not None: 566 activity.handlemessage( 567 SpazBotDiedMessage(self, killerplayer, msg.how) 568 ) 569 super().handlemessage(msg) # Augment standard behavior. 570 571 # Keep track of the player who last hit us for point rewarding. 572 elif isinstance(msg, bs.HitMessage): 573 source_player = msg.get_source_player(bs.Player) 574 if source_player: 575 self.last_player_attacked_by = source_player 576 self.last_attacked_time = bs.time() 577 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 578 super().handlemessage(msg) 579 else: 580 super().handlemessage(msg)
A really dumb AI version of bs.Spaz.
Category: Bot Classes
Add these to a bs.BotSet to use them.
Note: currently the AI has no real ability to navigate obstacles and so should only be used on wide-open maps.
When a SpazBot is killed, it delivers a bs.SpazBotDiedMessage to the current activity.
When a SpazBot is punched, it delivers a bs.SpazBotPunchedMessage to the current activity.
112 def __init__(self) -> None: 113 """Instantiate a spaz-bot.""" 114 super().__init__( 115 color=self.color, 116 highlight=self.highlight, 117 character=self.character, 118 source_player=None, 119 start_invincible=False, 120 can_accept_powerups=False, 121 ) 122 123 # If you need to add custom behavior to a bot, set this to a callable 124 # which takes one arg (the bot) and returns False if the bot's normal 125 # update should be run and True if not. 126 self.update_callback: Callable[[SpazBot], Any] | None = None 127 activity = self.activity 128 assert isinstance(activity, bs.GameActivity) 129 self._map = weakref.ref(activity.map) 130 self.last_player_attacked_by: bs.Player | None = None 131 self.last_attacked_time = 0.0 132 self.last_attacked_type: tuple[str, str] | None = None 133 self.target_point_default: bs.Vec3 | None = None 134 self.held_count = 0 135 self.last_player_held_by: bs.Player | None = None 136 self.target_flag: Flag | None = None 137 self._charge_speed = 0.5 * ( 138 self.charge_speed_min + self.charge_speed_max 139 ) 140 self._lead_amount = 0.5 141 self._mode = 'wait' 142 self._charge_closing_in = False 143 self._last_charge_dist = 0.0 144 self._running = False 145 self._last_jump_time = 0.0 146 147 self._throw_release_time: float | None = None 148 self._have_dropped_throw_bomb: bool | None = None 149 self._player_pts: list[tuple[bs.Vec3, bs.Vec3]] | None = None 150 151 # These cooldowns didn't exist when these bots were calibrated, 152 # so take them out of the equation. 153 self._jump_cooldown = 0 154 self._pickup_cooldown = 0 155 self._fly_cooldown = 0 156 self._bomb_cooldown = 0 157 158 if self.start_cursed: 159 self.curse()
Instantiate a spaz-bot.
199 def set_player_points(self, pts: list[tuple[bs.Vec3, bs.Vec3]]) -> None: 200 """Provide the spaz-bot with the locations of its enemies.""" 201 self._player_pts = pts
Provide the spaz-bot with the locations of its enemies.
203 def update_ai(self) -> None: 204 """Should be called periodically to update the spaz' AI.""" 205 # pylint: disable=too-many-branches 206 # pylint: disable=too-many-statements 207 # pylint: disable=too-many-locals 208 if self.update_callback is not None: 209 if self.update_callback(self): 210 # Bot has been handled. 211 return 212 213 if not self.node: 214 return 215 216 pos = self.node.position 217 our_pos = bs.Vec3(pos[0], 0, pos[2]) 218 can_attack = True 219 220 target_pt_raw: bs.Vec3 | None 221 target_vel: bs.Vec3 | None 222 223 # If we're a flag-bearer, we're pretty simple-minded - just walk 224 # towards the flag and try to pick it up. 225 if self.target_flag: 226 if self.node.hold_node: 227 holding_flag = self.node.hold_node.getnodetype() == 'flag' 228 else: 229 holding_flag = False 230 231 # If we're holding the flag, just walk left. 232 if holding_flag: 233 # Just walk left. 234 self.node.move_left_right = -1.0 235 self.node.move_up_down = 0.0 236 237 # Otherwise try to go pick it up. 238 elif self.target_flag.node: 239 target_pt_raw = bs.Vec3(*self.target_flag.node.position) 240 diff = target_pt_raw - our_pos 241 diff = bs.Vec3(diff[0], 0, diff[2]) # Don't care about y. 242 dist = diff.length() 243 to_target = diff.normalized() 244 245 # If we're holding some non-flag item, drop it. 246 if self.node.hold_node: 247 self.node.pickup_pressed = True 248 self.node.pickup_pressed = False 249 return 250 251 # If we're a runner, run only when not super-near the flag. 252 if self.run and dist > 3.0: 253 self._running = True 254 self.node.run = 1.0 255 else: 256 self._running = False 257 self.node.run = 0.0 258 259 self.node.move_left_right = to_target.x 260 self.node.move_up_down = -to_target.z 261 if dist < 1.25: 262 self.node.pickup_pressed = True 263 self.node.pickup_pressed = False 264 return 265 266 # Not a flag-bearer. If we're holding anything but a bomb, drop it. 267 if self.node.hold_node: 268 holding_bomb = self.node.hold_node.getnodetype() in ['bomb', 'prop'] 269 if not holding_bomb: 270 self.node.pickup_pressed = True 271 self.node.pickup_pressed = False 272 return 273 274 target_pt_raw, target_vel = self._get_target_player_pt() 275 276 if target_pt_raw is None: 277 # Use default target if we've got one. 278 if self.target_point_default is not None: 279 target_pt_raw = self.target_point_default 280 target_vel = bs.Vec3(0, 0, 0) 281 can_attack = False 282 283 # With no target, we stop moving and drop whatever we're holding. 284 else: 285 self.node.move_left_right = 0 286 self.node.move_up_down = 0 287 if self.node.hold_node: 288 self.node.pickup_pressed = True 289 self.node.pickup_pressed = False 290 return 291 292 # We don't want height to come into play. 293 target_pt_raw[1] = 0.0 294 assert target_vel is not None 295 target_vel[1] = 0.0 296 297 dist_raw = (target_pt_raw - our_pos).length() 298 299 # Use a point out in front of them as real target. 300 # (more out in front the farther from us they are) 301 target_pt = ( 302 target_pt_raw + target_vel * dist_raw * 0.3 * self._lead_amount 303 ) 304 305 diff = target_pt - our_pos 306 dist = diff.length() 307 to_target = diff.normalized() 308 309 if self._mode == 'throw': 310 # We can only throw if alive and well. 311 if not self._dead and not self.node.knockout: 312 assert self._throw_release_time is not None 313 time_till_throw = self._throw_release_time - bs.time() 314 315 if not self.node.hold_node: 316 # If we haven't thrown yet, whip out the bomb. 317 if not self._have_dropped_throw_bomb: 318 self.drop_bomb() 319 self._have_dropped_throw_bomb = True 320 321 # Otherwise our lack of held node means we successfully 322 # released our bomb; lets retreat now. 323 else: 324 self._mode = 'flee' 325 326 # Oh crap, we're holding a bomb; better throw it. 327 elif time_till_throw <= 0.0: 328 # Jump and throw. 329 def _safe_pickup(node: bs.Node) -> None: 330 if node and self.node: 331 self.node.pickup_pressed = True 332 self.node.pickup_pressed = False 333 334 if dist > 5.0: 335 self.node.jump_pressed = True 336 self.node.jump_pressed = False 337 338 # Throws: 339 bs.timer(0.1, bs.Call(_safe_pickup, self.node)) 340 else: 341 # Throws: 342 bs.timer(0.1, bs.Call(_safe_pickup, self.node)) 343 344 if self.static: 345 if time_till_throw < 0.3: 346 speed = 1.0 347 elif time_till_throw < 0.7 and dist > 3.0: 348 speed = -1.0 # Whiplash for long throws. 349 else: 350 speed = 0.02 351 else: 352 if time_till_throw < 0.7: 353 # Right before throw charge full speed towards target. 354 speed = 1.0 355 else: 356 # Earlier we can hold or move backward for a whiplash. 357 speed = 0.0125 358 self.node.move_left_right = to_target.x * speed 359 self.node.move_up_down = to_target.z * -1.0 * speed 360 361 elif self._mode == 'charge': 362 if random.random() < 0.3: 363 self._charge_speed = random.uniform( 364 self.charge_speed_min, self.charge_speed_max 365 ) 366 367 # If we're a runner we run during charges *except when near 368 # an edge (otherwise we tend to fly off easily). 369 if self.run and dist_raw > self.run_dist_min: 370 self._lead_amount = 0.3 371 self._running = True 372 self.node.run = 1.0 373 else: 374 self._lead_amount = 0.01 375 self._running = False 376 self.node.run = 0.0 377 378 self.node.move_left_right = to_target.x * self._charge_speed 379 self.node.move_up_down = to_target.z * -1.0 * self._charge_speed 380 381 elif self._mode == 'wait': 382 # Every now and then, aim towards our target. 383 # Other than that, just stand there. 384 if int(bs.time() * 1000.0) % 1234 < 100: 385 self.node.move_left_right = to_target.x * (400.0 / 33000) 386 self.node.move_up_down = to_target.z * (-400.0 / 33000) 387 else: 388 self.node.move_left_right = 0 389 self.node.move_up_down = 0 390 391 elif self._mode == 'flee': 392 # Even if we're a runner, only run till we get away from our 393 # target (if we keep running we tend to run off edges). 394 if self.run and dist < 3.0: 395 self._running = True 396 self.node.run = 1.0 397 else: 398 self._running = False 399 self.node.run = 0.0 400 self.node.move_left_right = to_target.x * -1.0 401 self.node.move_up_down = to_target.z 402 403 # We might wanna switch states unless we're doing a throw 404 # (in which case that's our sole concern). 405 if self._mode != 'throw': 406 # If we're currently charging, keep track of how far we are 407 # from our target. When this value increases it means our charge 408 # is over (ran by them or something). 409 if self._mode == 'charge': 410 if ( 411 self._charge_closing_in 412 and self._last_charge_dist < dist < 3.0 413 ): 414 self._charge_closing_in = False 415 self._last_charge_dist = dist 416 417 # If we have a clean shot, throw! 418 if ( 419 self.throw_dist_min <= dist < self.throw_dist_max 420 and random.random() < self.throwiness 421 and can_attack 422 ): 423 self._mode = 'throw' 424 self._lead_amount = ( 425 (0.4 + random.random() * 0.6) 426 if dist_raw > 4.0 427 else (0.1 + random.random() * 0.4) 428 ) 429 self._have_dropped_throw_bomb = False 430 self._throw_release_time = bs.time() + ( 431 1.0 / self.throw_rate 432 ) * (0.8 + 1.3 * random.random()) 433 434 # If we're static, always charge (which for us means barely move). 435 elif self.static: 436 self._mode = 'wait' 437 438 # If we're too close to charge (and aren't in the middle of an 439 # existing charge) run away. 440 elif dist < self.charge_dist_min and not self._charge_closing_in: 441 # ..unless we're near an edge, in which case we've got no 442 # choice but to charge. 443 if self.map.is_point_near_edge(our_pos, self._running): 444 if self._mode != 'charge': 445 self._mode = 'charge' 446 self._lead_amount = 0.2 447 self._charge_closing_in = True 448 self._last_charge_dist = dist 449 else: 450 self._mode = 'flee' 451 452 # We're within charging distance, backed against an edge, 453 # or farther than our max throw distance.. chaaarge! 454 elif ( 455 dist < self.charge_dist_max 456 or dist > self.throw_dist_max 457 or self.map.is_point_near_edge(our_pos, self._running) 458 ): 459 if self._mode != 'charge': 460 self._mode = 'charge' 461 self._lead_amount = 0.01 462 self._charge_closing_in = True 463 self._last_charge_dist = dist 464 465 # We're too close to throw but too far to charge - either run 466 # away or just chill if we're near an edge. 467 elif dist < self.throw_dist_min: 468 # Charge if either we're within charge range or 469 # cant retreat to throw. 470 self._mode = 'flee' 471 472 # Do some awesome jumps if we're running. 473 # FIXME: pylint: disable=too-many-boolean-expressions 474 if ( 475 self._running 476 and 1.2 < dist < 2.2 477 and bs.time() - self._last_jump_time > 1.0 478 ) or ( 479 self.bouncy 480 and bs.time() - self._last_jump_time > 0.4 481 and random.random() < 0.5 482 ): 483 self._last_jump_time = bs.time() 484 self.node.jump_pressed = True 485 self.node.jump_pressed = False 486 487 # Throw punches when real close. 488 if dist < (1.6 if self._running else 1.2) and can_attack: 489 if random.random() < self.punchiness: 490 self.on_punch_press() 491 self.on_punch_release()
Should be called periodically to update the spaz' AI.
493 def on_punched(self, damage: int) -> None: 494 """ 495 Method override; sends bs.SpazBotPunchedMessage 496 to the current activity. 497 """ 498 bs.getactivity().handlemessage(SpazBotPunchedMessage(self, damage))
Method override; sends bs.SpazBotPunchedMessage to the current activity.
500 def on_expire(self) -> None: 501 super().on_expire() 502 503 # We're being torn down; release our callback(s) so there's 504 # no chance of them keeping activities or other things alive. 505 self.update_callback = None
Called for remaining bascenev1.Actor
s when their activity dies.
Actors can use this opportunity to clear callbacks or other references which have the potential of keeping the bascenev1.Activity alive inadvertently (Activities can not exit cleanly while any Python references to them remain.)
Once an actor is expired (see bascenev1.Actor.is_expired()) it should no longer perform any game-affecting operations (creating, modifying, or deleting nodes, media, timers, etc.) Attempts to do so will likely result in errors.
507 def handlemessage(self, msg: Any) -> Any: 508 # pylint: disable=too-many-branches 509 assert not self.expired 510 511 # Keep track of if we're being held and by who most recently. 512 if isinstance(msg, bs.PickedUpMessage): 513 super().handlemessage(msg) # Augment standard behavior. 514 self.held_count += 1 515 picked_up_by = msg.node.source_player 516 if picked_up_by: 517 self.last_player_held_by = picked_up_by 518 519 elif isinstance(msg, bs.DroppedMessage): 520 super().handlemessage(msg) # Augment standard behavior. 521 self.held_count -= 1 522 if self.held_count < 0: 523 print('ERROR: spaz held_count < 0') 524 525 # Let's count someone dropping us as an attack. 526 try: 527 if msg.node: 528 picked_up_by = msg.node.source_player 529 else: 530 picked_up_by = None 531 except Exception: 532 logging.exception('Error on SpazBot DroppedMessage.') 533 picked_up_by = None 534 535 if picked_up_by: 536 self.last_player_attacked_by = picked_up_by 537 self.last_attacked_time = bs.time() 538 self.last_attacked_type = ('picked_up', 'default') 539 540 elif isinstance(msg, bs.DieMessage): 541 # Report normal deaths for scoring purposes. 542 if not self._dead and not msg.immediate: 543 killerplayer: bs.Player | None 544 545 # If this guy was being held at the time of death, the 546 # holder is the killer. 547 if self.held_count > 0 and self.last_player_held_by: 548 killerplayer = self.last_player_held_by 549 else: 550 # If they were attacked by someone in the last few 551 # seconds that person's the killer. 552 # Otherwise it was a suicide. 553 if ( 554 self.last_player_attacked_by 555 and bs.time() - self.last_attacked_time < 4.0 556 ): 557 killerplayer = self.last_player_attacked_by 558 else: 559 killerplayer = None 560 activity = self._activity() 561 562 # (convert dead player refs to None) 563 if not killerplayer: 564 killerplayer = None 565 if activity is not None: 566 activity.handlemessage( 567 SpazBotDiedMessage(self, killerplayer, msg.how) 568 ) 569 super().handlemessage(msg) # Augment standard behavior. 570 571 # Keep track of the player who last hit us for point rewarding. 572 elif isinstance(msg, bs.HitMessage): 573 source_player = msg.get_source_player(bs.Player) 574 if source_player: 575 self.last_player_attacked_by = source_player 576 self.last_attacked_time = bs.time() 577 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 578 super().handlemessage(msg) 579 else: 580 super().handlemessage(msg)
General message handling; can be passed any message object.
Inherited Members
- bascenev1lib.actor.spaz.Spaz
- node
- points_mult
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
583class BomberBot(SpazBot): 584 """A bot that throws regular bombs and occasionally punches. 585 586 category: Bot Classes 587 """ 588 589 character = 'Spaz' 590 punchiness = 0.3
A bot that throws regular bombs and occasionally punches.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- run
- charge_dist_min
- charge_dist_max
- run_dist_min
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- points_mult
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
593class BomberBotLite(BomberBot): 594 """A less aggressive yellow version of bs.BomberBot. 595 596 category: Bot Classes 597 """ 598 599 color = LITE_BOT_COLOR 600 highlight = LITE_BOT_HIGHLIGHT 601 punchiness = 0.2 602 throw_rate = 0.7 603 throwiness = 0.1 604 charge_speed_min = 0.6 605 charge_speed_max = 0.6
A less aggressive yellow version of bs.BomberBot.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- static
- bouncy
- run
- charge_dist_min
- charge_dist_max
- run_dist_min
- throw_dist_min
- throw_dist_max
- default_bomb_type
- default_bomb_count
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- points_mult
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
608class BomberBotStaticLite(BomberBotLite): 609 """A less aggressive generally immobile weak version of bs.BomberBot. 610 611 category: Bot Classes 612 """ 613 614 static = True 615 throw_dist_min = 0.0
A less aggressive generally immobile weak version of bs.BomberBot.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- bouncy
- run
- charge_dist_min
- charge_dist_max
- run_dist_min
- throw_dist_max
- default_bomb_type
- default_bomb_count
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- points_mult
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
618class BomberBotStatic(BomberBot): 619 """A version of bs.BomberBot who generally stays in one place. 620 621 category: Bot Classes 622 """ 623 624 static = True 625 throw_dist_min = 0.0
A version of bs.BomberBot who generally stays in one place.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- bouncy
- run
- charge_dist_min
- charge_dist_max
- run_dist_min
- charge_speed_min
- charge_speed_max
- throw_dist_max
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- points_mult
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
628class BomberBotPro(BomberBot): 629 """A more powerful version of bs.BomberBot. 630 631 category: Bot Classes 632 """ 633 634 points_mult = 2 635 color = PRO_BOT_COLOR 636 highlight = PRO_BOT_HIGHLIGHT 637 default_bomb_count = 3 638 default_boxing_gloves = True 639 punchiness = 0.7 640 throw_rate = 1.3 641 run = True 642 run_dist_min = 6.0
A more powerful version of bs.BomberBot.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- default_bomb_type
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
645class BomberBotProShielded(BomberBotPro): 646 """A more powerful version of bs.BomberBot who starts with shields. 647 648 category: Bot Classes 649 """ 650 651 points_mult = 3 652 default_shields = True
A more powerful version of bs.BomberBot who starts with shields.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- default_bomb_type
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- BomberBotPro
- color
- highlight
- default_bomb_count
- default_boxing_gloves
- punchiness
- throw_rate
- run
- run_dist_min
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
655class BomberBotProStatic(BomberBotPro): 656 """A more powerful bs.BomberBot who generally stays in one place. 657 658 category: Bot Classes 659 """ 660 661 static = True 662 throw_dist_min = 0.0
A more powerful bs.BomberBot who generally stays in one place.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- bouncy
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_max
- default_bomb_type
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- BomberBotPro
- points_mult
- color
- highlight
- default_bomb_count
- default_boxing_gloves
- punchiness
- throw_rate
- run
- run_dist_min
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
665class BomberBotProStaticShielded(BomberBotProShielded): 666 """A powerful bs.BomberBot with shields who is generally immobile. 667 668 category: Bot Classes 669 """ 670 671 static = True 672 throw_dist_min = 0.0
A powerful bs.BomberBot with shields who is generally immobile.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- bouncy
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_max
- default_bomb_type
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- BomberBotPro
- color
- highlight
- default_bomb_count
- default_boxing_gloves
- punchiness
- throw_rate
- run
- run_dist_min
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
675class BrawlerBot(SpazBot): 676 """A bot who walks and punches things. 677 678 category: Bot Classes 679 """ 680 681 character = 'Kronk' 682 punchiness = 0.9 683 charge_dist_max = 9999.0 684 charge_speed_min = 1.0 685 charge_speed_max = 1.0 686 throw_dist_min = 9999 687 throw_dist_max = 9999
A bot who walks and punches things.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- run
- charge_dist_min
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- points_mult
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
690class BrawlerBotLite(BrawlerBot): 691 """A weaker version of bs.BrawlerBot. 692 693 category: Bot Classes 694 """ 695 696 color = LITE_BOT_COLOR 697 highlight = LITE_BOT_HIGHLIGHT 698 punchiness = 0.3 699 charge_speed_min = 0.6 700 charge_speed_max = 0.6
A weaker version of bs.BrawlerBot.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- run
- charge_dist_min
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- points_mult
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
703class BrawlerBotPro(BrawlerBot): 704 """A stronger version of bs.BrawlerBot. 705 706 category: Bot Classes 707 """ 708 709 color = PRO_BOT_COLOR 710 highlight = PRO_BOT_HIGHLIGHT 711 run = True 712 run_dist_min = 4.0 713 default_boxing_gloves = True 714 punchiness = 0.95 715 points_mult = 2
A stronger version of bs.BrawlerBot.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- charge_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- BrawlerBot
- character
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
718class BrawlerBotProShielded(BrawlerBotPro): 719 """A stronger version of bs.BrawlerBot who starts with shields. 720 721 category: Bot Classes 722 """ 723 724 default_shields = True 725 points_mult = 3
A stronger version of bs.BrawlerBot who starts with shields.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- charge_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- BrawlerBot
- character
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
728class ChargerBot(SpazBot): 729 """A speedy melee attack bot. 730 731 category: Bot Classes 732 """ 733 734 character = 'Snake Shadow' 735 punchiness = 1.0 736 run = True 737 charge_dist_min = 10.0 738 charge_dist_max = 9999.0 739 charge_speed_min = 1.0 740 charge_speed_max = 1.0 741 throw_dist_min = 9999 742 throw_dist_max = 9999 743 points_mult = 2
A speedy melee attack bot.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
746class BouncyBot(SpazBot): 747 """A speedy attacking melee bot that jumps constantly. 748 749 category: Bot Classes 750 """ 751 752 color = (1, 1, 1) 753 highlight = (1.0, 0.5, 0.5) 754 character = 'Easter Bunny' 755 punchiness = 1.0 756 run = True 757 bouncy = True 758 default_boxing_gloves = True 759 charge_dist_min = 10.0 760 charge_dist_max = 9999.0 761 charge_speed_min = 1.0 762 charge_speed_max = 1.0 763 throw_dist_min = 9999 764 throw_dist_max = 9999 765 points_mult = 2
A speedy attacking melee bot that jumps constantly.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
768class ChargerBotPro(ChargerBot): 769 """A stronger bs.ChargerBot. 770 771 category: Bot Classes 772 """ 773 774 color = PRO_BOT_COLOR 775 highlight = PRO_BOT_HIGHLIGHT 776 default_shields = True 777 default_boxing_gloves = True 778 points_mult = 3
A stronger bs.ChargerBot.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- ChargerBot
- character
- punchiness
- run
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
781class ChargerBotProShielded(ChargerBotPro): 782 """A stronger bs.ChargerBot who starts with shields. 783 784 category: Bot Classes 785 """ 786 787 default_shields = True 788 points_mult = 4
A stronger bs.ChargerBot who starts with shields.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- throwiness
- static
- bouncy
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- ChargerBot
- character
- punchiness
- run
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
791class TriggerBot(SpazBot): 792 """A slow moving bot with trigger bombs. 793 794 category: Bot Classes 795 """ 796 797 character = 'Zoe' 798 punchiness = 0.75 799 throwiness = 0.7 800 charge_dist_max = 1.0 801 charge_speed_min = 0.3 802 charge_speed_max = 0.5 803 throw_dist_min = 3.5 804 throw_dist_max = 5.5 805 default_bomb_type = 'impact' 806 points_mult = 2
A slow moving bot with trigger bombs.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- static
- bouncy
- run
- charge_dist_min
- run_dist_min
- throw_rate
- default_bomb_count
- start_cursed
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
809class TriggerBotStatic(TriggerBot): 810 """A bs.TriggerBot who generally stays in one place. 811 812 category: Bot Classes 813 """ 814 815 static = True 816 throw_dist_min = 0.0
A bs.TriggerBot who generally stays in one place.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- bouncy
- run
- charge_dist_min
- run_dist_min
- throw_rate
- default_bomb_count
- start_cursed
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- TriggerBot
- character
- punchiness
- throwiness
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_max
- default_bomb_type
- points_mult
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
819class TriggerBotPro(TriggerBot): 820 """A stronger version of bs.TriggerBot. 821 822 category: Bot Classes 823 """ 824 825 color = PRO_BOT_COLOR 826 highlight = PRO_BOT_HIGHLIGHT 827 default_bomb_count = 3 828 default_boxing_gloves = True 829 charge_speed_min = 1.0 830 charge_speed_max = 1.0 831 punchiness = 0.9 832 throw_rate = 1.3 833 run = True 834 run_dist_min = 6.0 835 points_mult = 3
A stronger version of bs.TriggerBot.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- static
- bouncy
- charge_dist_min
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
838class TriggerBotProShielded(TriggerBotPro): 839 """A stronger version of bs.TriggerBot who starts with shields. 840 841 category: Bot Classes 842 """ 843 844 default_shields = True 845 points_mult = 4
A stronger version of bs.TriggerBot who starts with shields.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- static
- bouncy
- charge_dist_min
- start_cursed
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- TriggerBotPro
- color
- highlight
- default_bomb_count
- default_boxing_gloves
- charge_speed_min
- charge_speed_max
- punchiness
- throw_rate
- run
- run_dist_min
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
848class StickyBot(SpazBot): 849 """A crazy bot who runs and throws sticky bombs. 850 851 category: Bot Classes 852 """ 853 854 character = 'Mel' 855 punchiness = 0.9 856 throwiness = 1.0 857 run = True 858 charge_dist_min = 4.0 859 charge_dist_max = 10.0 860 charge_speed_min = 1.0 861 charge_speed_max = 1.0 862 throw_dist_min = 0.0 863 throw_dist_max = 4.0 864 throw_rate = 2.0 865 default_bomb_type = 'sticky' 866 default_bomb_count = 3 867 points_mult = 3
A crazy bot who runs and throws sticky bombs.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- static
- bouncy
- run_dist_min
- start_cursed
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
870class StickyBotStatic(StickyBot): 871 """A crazy bot who throws sticky-bombs but generally stays in one place. 872 873 category: Bot Classes 874 """ 875 876 static = True
A crazy bot who throws sticky-bombs but generally stays in one place.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- bouncy
- run_dist_min
- start_cursed
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- StickyBot
- character
- punchiness
- throwiness
- run
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- throw_rate
- default_bomb_type
- default_bomb_count
- points_mult
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
879class ExplodeyBot(SpazBot): 880 """A bot who runs and explodes in 5 seconds. 881 882 category: Bot Classes 883 """ 884 885 character = 'Jack Morgan' 886 run = True 887 charge_dist_min = 0.0 888 charge_dist_max = 9999 889 charge_speed_min = 1.0 890 charge_speed_max = 1.0 891 throw_dist_min = 9999 892 throw_dist_max = 9999 893 start_cursed = True 894 points_mult = 4
A bot who runs and explodes in 5 seconds.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- punchiness
- throwiness
- static
- bouncy
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
897class ExplodeyBotNoTimeLimit(ExplodeyBot): 898 """A bot who runs but does not explode on his own. 899 900 category: Bot Classes 901 """ 902 903 curse_time = None
A bot who runs but does not explode on his own.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- punchiness
- throwiness
- static
- bouncy
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- ExplodeyBot
- character
- run
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- start_cursed
- points_mult
- bascenev1lib.actor.spaz.Spaz
- node
- default_boxing_gloves
- default_shields
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
906class ExplodeyBotShielded(ExplodeyBot): 907 """A bs.ExplodeyBot who starts with shields. 908 909 category: Bot Classes 910 """ 911 912 default_shields = True 913 points_mult = 5
A bs.ExplodeyBot who starts with shields.
category: Bot Classes
Inherited Members
- SpazBot
- SpazBot
- punchiness
- throwiness
- static
- bouncy
- run_dist_min
- throw_rate
- default_bomb_type
- default_bomb_count
- color
- highlight
- update_callback
- last_player_attacked_by
- last_attacked_time
- last_attacked_type
- target_point_default
- held_count
- last_player_held_by
- target_flag
- map
- set_player_points
- update_ai
- on_punched
- on_expire
- handlemessage
- ExplodeyBot
- character
- run
- charge_dist_min
- charge_dist_max
- charge_speed_min
- charge_speed_max
- throw_dist_min
- throw_dist_max
- start_cursed
- bascenev1lib.actor.spaz.Spaz
- node
- curse_time
- default_boxing_gloves
- play_big_death_sound
- impact_scale
- source_player
- fly
- shield
- hitpoints
- hitpoints_max
- shield_hitpoints
- shield_hitpoints_max
- shield_decay_rate
- shield_decay_timer
- bomb_count
- bomb_type_default
- bomb_type
- land_mine_count
- blast_radius
- powerups_expire
- last_punch_time_ms
- last_pickup_time_ms
- last_jump_time_ms
- last_run_time_ms
- last_bomb_time_ms
- frozen
- shattered
- punch_callback
- pick_up_powerup_callback
- exists
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- bascenev1._actor.Actor
- autoretain
- expired
- activity
- getactivity
916class SpazBotSet: 917 """A container/controller for one or more bs.SpazBots. 918 919 category: Bot Classes 920 """ 921 922 def __init__(self) -> None: 923 """Create a bot-set.""" 924 925 # We spread our bots out over a few lists so we can update 926 # them in a staggered fashion. 927 self._bot_list_count = 5 928 self._bot_add_list = 0 929 self._bot_update_list = 0 930 self._bot_lists: list[list[SpazBot]] = [ 931 [] for _ in range(self._bot_list_count) 932 ] 933 self._spawn_sound = bs.getsound('spawn') 934 self._spawning_count = 0 935 self._bot_update_timer: bs.Timer | None = None 936 self.start_moving() 937 938 def __del__(self) -> None: 939 self.clear() 940 941 def spawn_bot( 942 self, 943 bot_type: type[SpazBot], 944 pos: Sequence[float], 945 spawn_time: float = 3.0, 946 on_spawn_call: Callable[[SpazBot], Any] | None = None, 947 ) -> None: 948 """Spawn a bot from this set.""" 949 from bascenev1lib.actor import spawner 950 951 spawner.Spawner( 952 pt=pos, 953 spawn_time=spawn_time, 954 send_spawn_message=False, 955 spawn_callback=bs.Call( 956 self._spawn_bot, bot_type, pos, on_spawn_call 957 ), 958 ) 959 self._spawning_count += 1 960 961 def _spawn_bot( 962 self, 963 bot_type: type[SpazBot], 964 pos: Sequence[float], 965 on_spawn_call: Callable[[SpazBot], Any] | None, 966 ) -> None: 967 spaz = bot_type() 968 self._spawn_sound.play(position=pos) 969 assert spaz.node 970 spaz.node.handlemessage('flash') 971 spaz.node.is_area_of_interest = False 972 spaz.handlemessage(bs.StandMessage(pos, random.uniform(0, 360))) 973 self.add_bot(spaz) 974 self._spawning_count -= 1 975 if on_spawn_call is not None: 976 on_spawn_call(spaz) 977 978 def have_living_bots(self) -> bool: 979 """Return whether any bots in the set are alive or spawning.""" 980 return self._spawning_count > 0 or any( 981 any(b.is_alive() for b in l) for l in self._bot_lists 982 ) 983 984 def get_living_bots(self) -> list[SpazBot]: 985 """Get the living bots in the set.""" 986 bots: list[SpazBot] = [] 987 for botlist in self._bot_lists: 988 for bot in botlist: 989 if bot.is_alive(): 990 bots.append(bot) 991 return bots 992 993 def _update(self) -> None: 994 # Update one of our bot lists each time through. 995 # First off, remove no-longer-existing bots from the list. 996 try: 997 bot_list = self._bot_lists[self._bot_update_list] = [ 998 b for b in self._bot_lists[self._bot_update_list] if b 999 ] 1000 except Exception: 1001 bot_list = [] 1002 logging.exception( 1003 'Error updating bot list: %s', 1004 self._bot_lists[self._bot_update_list], 1005 ) 1006 self._bot_update_list = ( 1007 self._bot_update_list + 1 1008 ) % self._bot_list_count 1009 1010 # Update our list of player points for the bots to use. 1011 player_pts = [] 1012 for player in bs.getactivity().players: 1013 assert isinstance(player, bs.Player) 1014 try: 1015 # TODO: could use abstracted player.position here so we 1016 # don't have to assume their actor type, but we have no 1017 # abstracted velocity as of yet. 1018 if player.is_alive(): 1019 assert isinstance(player.actor, Spaz) 1020 assert player.actor.node 1021 player_pts.append( 1022 ( 1023 bs.Vec3(player.actor.node.position), 1024 bs.Vec3(player.actor.node.velocity), 1025 ) 1026 ) 1027 except Exception: 1028 logging.exception('Error on bot-set _update.') 1029 1030 for bot in bot_list: 1031 bot.set_player_points(player_pts) 1032 bot.update_ai() 1033 1034 def clear(self) -> None: 1035 """Immediately clear out any bots in the set.""" 1036 1037 # Don't do this if the activity is shutting down or dead. 1038 activity = bs.getactivity(doraise=False) 1039 if activity is None or activity.expired: 1040 return 1041 1042 for i, bot_list in enumerate(self._bot_lists): 1043 for bot in bot_list: 1044 bot.handlemessage(bs.DieMessage(immediate=True)) 1045 self._bot_lists[i] = [] 1046 1047 def start_moving(self) -> None: 1048 """Start processing bot AI updates so they start doing their thing.""" 1049 self._bot_update_timer = bs.Timer( 1050 0.05, bs.WeakCall(self._update), repeat=True 1051 ) 1052 1053 def stop_moving(self) -> None: 1054 """Tell all bots to stop moving and stops updating their AI. 1055 1056 Useful when players have won and you want the 1057 enemy bots to just stand and look bewildered. 1058 """ 1059 self._bot_update_timer = None 1060 for botlist in self._bot_lists: 1061 for bot in botlist: 1062 if bot.node: 1063 bot.node.move_left_right = 0 1064 bot.node.move_up_down = 0 1065 1066 def celebrate(self, duration: float) -> None: 1067 """Tell all living bots in the set to celebrate momentarily. 1068 1069 Duration is given in seconds. 1070 """ 1071 msg = bs.CelebrateMessage(duration=duration) 1072 for botlist in self._bot_lists: 1073 for bot in botlist: 1074 if bot: 1075