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, override 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 @override 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 @override 501 def on_expire(self) -> None: 502 super().on_expire() 503 504 # We're being torn down; release our callback(s) so there's 505 # no chance of them keeping activities or other things alive. 506 self.update_callback = None 507 508 @override 509 def handlemessage(self, msg: Any) -> Any: 510 # pylint: disable=too-many-branches 511 assert not self.expired 512 513 # Keep track of if we're being held and by who most recently. 514 if isinstance(msg, bs.PickedUpMessage): 515 super().handlemessage(msg) # Augment standard behavior. 516 self.held_count += 1 517 picked_up_by = msg.node.source_player 518 if picked_up_by: 519 self.last_player_held_by = picked_up_by 520 521 elif isinstance(msg, bs.DroppedMessage): 522 super().handlemessage(msg) # Augment standard behavior. 523 self.held_count -= 1 524 if self.held_count < 0: 525 print('ERROR: spaz held_count < 0') 526 527 # Let's count someone dropping us as an attack. 528 try: 529 if msg.node: 530 picked_up_by = msg.node.source_player 531 else: 532 picked_up_by = None 533 except Exception: 534 logging.exception('Error on SpazBot DroppedMessage.') 535 picked_up_by = None 536 537 if picked_up_by: 538 self.last_player_attacked_by = picked_up_by 539 self.last_attacked_time = bs.time() 540 self.last_attacked_type = ('picked_up', 'default') 541 542 elif isinstance(msg, bs.DieMessage): 543 # Report normal deaths for scoring purposes. 544 if not self._dead and not msg.immediate: 545 killerplayer: bs.Player | None 546 547 # If this guy was being held at the time of death, the 548 # holder is the killer. 549 if self.held_count > 0 and self.last_player_held_by: 550 killerplayer = self.last_player_held_by 551 else: 552 # If they were attacked by someone in the last few 553 # seconds that person's the killer. 554 # Otherwise it was a suicide. 555 if ( 556 self.last_player_attacked_by 557 and bs.time() - self.last_attacked_time < 4.0 558 ): 559 killerplayer = self.last_player_attacked_by 560 else: 561 killerplayer = None 562 activity = self._activity() 563 564 # (convert dead player refs to None) 565 if not killerplayer: 566 killerplayer = None 567 if activity is not None: 568 activity.handlemessage( 569 SpazBotDiedMessage(self, killerplayer, msg.how) 570 ) 571 super().handlemessage(msg) # Augment standard behavior. 572 573 # Keep track of the player who last hit us for point rewarding. 574 elif isinstance(msg, bs.HitMessage): 575 source_player = msg.get_source_player(bs.Player) 576 if source_player: 577 self.last_player_attacked_by = source_player 578 self.last_attacked_time = bs.time() 579 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 580 super().handlemessage(msg) 581 else: 582 super().handlemessage(msg) 583 584 585class BomberBot(SpazBot): 586 """A bot that throws regular bombs and occasionally punches. 587 588 category: Bot Classes 589 """ 590 591 character = 'Spaz' 592 punchiness = 0.3 593 594 595class BomberBotLite(BomberBot): 596 """A less aggressive yellow version of bs.BomberBot. 597 598 category: Bot Classes 599 """ 600 601 color = LITE_BOT_COLOR 602 highlight = LITE_BOT_HIGHLIGHT 603 punchiness = 0.2 604 throw_rate = 0.7 605 throwiness = 0.1 606 charge_speed_min = 0.6 607 charge_speed_max = 0.6 608 609 610class BomberBotStaticLite(BomberBotLite): 611 """A less aggressive generally immobile weak version of bs.BomberBot. 612 613 category: Bot Classes 614 """ 615 616 static = True 617 throw_dist_min = 0.0 618 619 620class BomberBotStatic(BomberBot): 621 """A version of bs.BomberBot who generally stays in one place. 622 623 category: Bot Classes 624 """ 625 626 static = True 627 throw_dist_min = 0.0 628 629 630class BomberBotPro(BomberBot): 631 """A more powerful version of bs.BomberBot. 632 633 category: Bot Classes 634 """ 635 636 points_mult = 2 637 color = PRO_BOT_COLOR 638 highlight = PRO_BOT_HIGHLIGHT 639 default_bomb_count = 3 640 default_boxing_gloves = True 641 punchiness = 0.7 642 throw_rate = 1.3 643 run = True 644 run_dist_min = 6.0 645 646 647class BomberBotProShielded(BomberBotPro): 648 """A more powerful version of bs.BomberBot who starts with shields. 649 650 category: Bot Classes 651 """ 652 653 points_mult = 3 654 default_shields = True 655 656 657class BomberBotProStatic(BomberBotPro): 658 """A more powerful bs.BomberBot who generally stays in one place. 659 660 category: Bot Classes 661 """ 662 663 static = True 664 throw_dist_min = 0.0 665 666 667class BomberBotProStaticShielded(BomberBotProShielded): 668 """A powerful bs.BomberBot with shields who is generally immobile. 669 670 category: Bot Classes 671 """ 672 673 static = True 674 throw_dist_min = 0.0 675 676 677class BrawlerBot(SpazBot): 678 """A bot who walks and punches things. 679 680 category: Bot Classes 681 """ 682 683 character = 'Kronk' 684 punchiness = 0.9 685 charge_dist_max = 9999.0 686 charge_speed_min = 1.0 687 charge_speed_max = 1.0 688 throw_dist_min = 9999 689 throw_dist_max = 9999 690 691 692class BrawlerBotLite(BrawlerBot): 693 """A weaker version of bs.BrawlerBot. 694 695 category: Bot Classes 696 """ 697 698 color = LITE_BOT_COLOR 699 highlight = LITE_BOT_HIGHLIGHT 700 punchiness = 0.3 701 charge_speed_min = 0.6 702 charge_speed_max = 0.6 703 704 705class BrawlerBotPro(BrawlerBot): 706 """A stronger version of bs.BrawlerBot. 707 708 category: Bot Classes 709 """ 710 711 color = PRO_BOT_COLOR 712 highlight = PRO_BOT_HIGHLIGHT 713 run = True 714 run_dist_min = 4.0 715 default_boxing_gloves = True 716 punchiness = 0.95 717 points_mult = 2 718 719 720class BrawlerBotProShielded(BrawlerBotPro): 721 """A stronger version of bs.BrawlerBot who starts with shields. 722 723 category: Bot Classes 724 """ 725 726 default_shields = True 727 points_mult = 3 728 729 730class ChargerBot(SpazBot): 731 """A speedy melee attack bot. 732 733 category: Bot Classes 734 """ 735 736 character = 'Snake Shadow' 737 punchiness = 1.0 738 run = True 739 charge_dist_min = 10.0 740 charge_dist_max = 9999.0 741 charge_speed_min = 1.0 742 charge_speed_max = 1.0 743 throw_dist_min = 9999 744 throw_dist_max = 9999 745 points_mult = 2 746 747 748class BouncyBot(SpazBot): 749 """A speedy attacking melee bot that jumps constantly. 750 751 category: Bot Classes 752 """ 753 754 color = (1, 1, 1) 755 highlight = (1.0, 0.5, 0.5) 756 character = 'Easter Bunny' 757 punchiness = 1.0 758 run = True 759 bouncy = True 760 default_boxing_gloves = True 761 charge_dist_min = 10.0 762 charge_dist_max = 9999.0 763 charge_speed_min = 1.0 764 charge_speed_max = 1.0 765 throw_dist_min = 9999 766 throw_dist_max = 9999 767 points_mult = 2 768 769 770class ChargerBotPro(ChargerBot): 771 """A stronger bs.ChargerBot. 772 773 category: Bot Classes 774 """ 775 776 color = PRO_BOT_COLOR 777 highlight = PRO_BOT_HIGHLIGHT 778 default_boxing_gloves = True 779 points_mult = 3 780 781 782class ChargerBotProShielded(ChargerBotPro): 783 """A stronger bs.ChargerBot who starts with shields. 784 785 category: Bot Classes 786 """ 787 788 default_shields = True 789 points_mult = 4 790 791 792class TriggerBot(SpazBot): 793 """A slow moving bot with trigger bombs. 794 795 category: Bot Classes 796 """ 797 798 character = 'Zoe' 799 punchiness = 0.75 800 throwiness = 0.7 801 charge_dist_max = 1.0 802 charge_speed_min = 0.3 803 charge_speed_max = 0.5 804 throw_dist_min = 3.5 805 throw_dist_max = 5.5 806 default_bomb_type = 'impact' 807 points_mult = 2 808 809 810class TriggerBotStatic(TriggerBot): 811 """A bs.TriggerBot who generally stays in one place. 812 813 category: Bot Classes 814 """ 815 816 static = True 817 throw_dist_min = 0.0 818 819 820class TriggerBotPro(TriggerBot): 821 """A stronger version of bs.TriggerBot. 822 823 category: Bot Classes 824 """ 825 826 color = PRO_BOT_COLOR 827 highlight = PRO_BOT_HIGHLIGHT 828 default_bomb_count = 3 829 default_boxing_gloves = True 830 charge_speed_min = 1.0 831 charge_speed_max = 1.0 832 punchiness = 0.9 833 throw_rate = 1.3 834 run = True 835 run_dist_min = 6.0 836 points_mult = 3 837 838 839class TriggerBotProShielded(TriggerBotPro): 840 """A stronger version of bs.TriggerBot who starts with shields. 841 842 category: Bot Classes 843 """ 844 845 default_shields = True 846 points_mult = 4 847 848 849class StickyBot(SpazBot): 850 """A crazy bot who runs and throws sticky bombs. 851 852 category: Bot Classes 853 """ 854 855 character = 'Mel' 856 punchiness = 0.9 857 throwiness = 1.0 858 run = True 859 charge_dist_min = 4.0 860 charge_dist_max = 10.0 861 charge_speed_min = 1.0 862 charge_speed_max = 1.0 863 throw_dist_min = 0.0 864 throw_dist_max = 4.0 865 throw_rate = 2.0 866 default_bomb_type = 'sticky' 867 default_bomb_count = 3 868 points_mult = 3 869 870 871class StickyBotStatic(StickyBot): 872 """A crazy bot who throws sticky-bombs but generally stays in one place. 873 874 category: Bot Classes 875 """ 876 877 static = True 878 879 880class ExplodeyBot(SpazBot): 881 """A bot who runs and explodes in 5 seconds. 882 883 category: Bot Classes 884 """ 885 886 character = 'Jack Morgan' 887 run = True 888 charge_dist_min = 0.0 889 charge_dist_max = 9999 890 charge_speed_min = 1.0 891 charge_speed_max = 1.0 892 throw_dist_min = 9999 893 throw_dist_max = 9999 894 start_cursed = True 895 points_mult = 4 896 897 898class ExplodeyBotNoTimeLimit(ExplodeyBot): 899 """A bot who runs but does not explode on his own. 900 901 category: Bot Classes 902 """ 903 904 curse_time = None 905 906 907class ExplodeyBotShielded(ExplodeyBot): 908 """A bs.ExplodeyBot who starts with shields. 909 910 category: Bot Classes 911 """ 912 913 default_shields = True 914 points_mult = 5 915 916 917class SpazBotSet: 918 """A container/controller for one or more bs.SpazBots. 919 920 category: Bot Classes 921 """ 922 923 def __init__(self) -> None: 924 """Create a bot-set.""" 925 926 # We spread our bots out over a few lists so we can update 927 # them in a staggered fashion. 928 self._bot_list_count = 5 929 self._bot_add_list = 0 930 self._bot_update_list = 0 931 self._bot_lists: list[list[SpazBot]] = [ 932 [] for _ in range(self._bot_list_count) 933 ] 934 self._spawn_sound = bs.getsound('spawn') 935 self._spawning_count = 0 936 self._bot_update_timer: bs.Timer | None = None 937 self.start_moving() 938 939 def __del__(self) -> None: 940 self.clear() 941 942 def spawn_bot( 943 self, 944 bot_type: type[SpazBot], 945 pos: Sequence[float], 946 spawn_time: float = 3.0, 947 on_spawn_call: Callable[[SpazBot], Any] | None = None, 948 ) -> None: 949 """Spawn a bot from this set.""" 950 from bascenev1lib.actor.spawner import Spawner 951 952 Spawner( 953 pt=pos, 954 spawn_time=spawn_time, 955 send_spawn_message=False, 956 spawn_callback=bs.Call( 957 self._spawn_bot, bot_type, pos, on_spawn_call 958 ), 959 ) 960 self._spawning_count += 1 961 962 def _spawn_bot( 963 self, 964 bot_type: type[SpazBot], 965 pos: Sequence[float], 966 on_spawn_call: Callable[[SpazBot], Any] | None, 967 ) -> None: 968 spaz = bot_type() 969 self._spawn_sound.play(position=pos) 970 assert spaz.node 971 spaz.node.handlemessage('flash') 972 spaz.node.is_area_of_interest = False 973 spaz.handlemessage(bs.StandMessage(pos, random.uniform(0, 360))) 974 self.add_bot(spaz) 975 self._spawning_count -= 1 976 if on_spawn_call is not None: 977 on_spawn_call(spaz) 978 979 def have_living_bots(self) -> bool: 980 """Return whether any bots in the set are alive or spawning.""" 981 return self._spawning_count > 0 or any( 982 any(b.is_alive() for b in l) for l in self._bot_lists 983 ) 984 985 def get_living_bots(self) -> list[SpazBot]: 986 """Get the living bots in the set.""" 987 bots: list[SpazBot] = [] 988 for botlist in self._bot_lists: 989 for bot in botlist: 990 if bot.is_alive(): 991 bots.append(bot) 992 return bots 993 994 def _update(self) -> None: 995 # Update one of our bot lists each time through. 996 # First off, remove no-longer-existing bots from the list. 997 try: 998 bot_list = self._bot_lists[self._bot_update_list] = [ 999 b for b in self._bot_lists[self._bot_update_list] if b 1000 ] 1001 except Exception: 1002 bot_list = [] 1003 logging.exception( 1004 'Error updating bot list: %s', 1005 self._bot_lists[self._bot_update_list], 1006 ) 1007 self._bot_update_list = ( 1008 self._bot_update_list + 1 1009 ) % self._bot_list_count 1010 1011 # Update our list of player points for the bots to use. 1012 player_pts = [] 1013 for player in bs.getactivity().players: 1014 assert isinstance(player, bs.Player) 1015 try: 1016 # TODO: could use abstracted player.position here so we 1017 # don't have to assume their actor type, but we have no 1018 # abstracted velocity as of yet. 1019 if player.is_alive(): 1020 assert isinstance(player.actor, Spaz) 1021 assert player.actor.node 1022 player_pts.append( 1023 ( 1024 bs.Vec3(player.actor.node.position), 1025 bs.Vec3(player.actor.node.velocity), 1026 ) 1027 ) 1028 except Exception: 1029 logging.exception('Error on bot-set _update.') 1030 1031 for bot in bot_list: 1032 bot.set_player_points(player_pts) 1033 bot.update_ai() 1034 1035 def clear(self) -> None: 1036 """Immediately clear out any bots in the set.""" 1037 1038 # Don't do this if the activity is shutting down or dead. 1039 activity = bs.getactivity(doraise=False) 1040 if activity is None or activity.expired: 1041 return 1042 1043 for i, bot_list in enumerate(self._bot_lists): 1044 for bot in bot_list: 1045 bot.handlemessage(bs.DieMessage(immediate=True)) 1046 self._bot_lists[i] = [] 1047 1048 def start_moving(self) -> None: 1049 """Start processing bot AI updates so they start doing their thing.""" 1050 self._bot_update_timer = bs.Timer( 1051 0.05, bs.WeakCall(self._update), repeat=True 1052 ) 1053 1054 def stop_moving(self) -> None: 1055 """Tell all bots to stop moving and stops updating their AI. 1056 1057 Useful when players have won and you want the 1058 enemy bots to just stand and look bewildered. 1059 """ 1060 self._bot_update_timer = None 1061 for botlist in self._bot_lists: 1062 for bot in botlist: 1063 if bot.node: 1064 bot.node.move_left_right = 0 1065 bot.node.move_up_down = 0 1066 1067 def celebrate(self, duration: float) -> None: 1068 """Tell all living bots in the set to celebrate momentarily. 1069 1070 Duration is given in seconds. 1071 """ 1072 msg = bs.CelebrateMessage(duration=duration) 1073 for botlist in self._bot_lists: 1074 for bot in botlist: 1075 if bot: 1076 bot.handlemessage(msg) 1077 1078 def final_celebrate(self) -> None: 1079 """Tell all bots in the set to stop what they were doing and celebrate. 1080 1081 Use this when the bots have won a game. 1082 """ 1083 self._bot_update_timer = None 1084 1085 # At this point stop doing anything but jumping and celebrating. 1086 for botlist in self._bot_lists: 1087 for bot in botlist: 1088 if bot: 1089 assert bot.node # (should exist if 'if bot' was True) 1090 bot.node.move_left_right = 0 1091 bot.node.move_up_down = 0 1092 bs.timer( 1093 0.5 * random.random(), 1094 bs.Call(bot.handlemessage, bs.CelebrateMessage()), 1095 ) 1096 jump_duration = random.randrange(400, 500) 1097 j = random.randrange(0, 200) 1098 for _i in range(10): 1099 bot.node.jump_pressed = True 1100 bot.node.jump_pressed = False 1101 j += jump_duration 1102 bs.timer( 1103 random.uniform(0.0, 1.0), 1104 bs.Call(bot.node.handlemessage, 'attack_sound'), 1105 ) 1106 bs.timer( 1107 random.uniform(1.0, 2.0), 1108 bs.Call(bot.node.handlemessage, 'attack_sound'), 1109 ) 1110 bs.timer( 1111 random.uniform(2.0, 3.0), 1112 bs.Call(bot.node.handlemessage, 'attack_sound'), 1113 ) 1114 1115 def add_bot(self, bot: SpazBot) -> None: 1116 """Add a bs.SpazBot instance to the set.""" 1117 self._bot_lists[self._bot_add_list].append(bot) 1118 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 @override 494 def on_punched(self, damage: int) -> None: 495 """ 496 Method override; sends bs.SpazBotPunchedMessage 497 to the current activity. 498 """ 499 bs.getactivity().handlemessage(SpazBotPunchedMessage(self, damage)) 500 501 @override 502 def on_expire(self) -> None: 503 super().on_expire() 504 505 # We're being torn down; release our callback(s) so there's 506 # no chance of them keeping activities or other things alive. 507 self.update_callback = None 508 509 @override 510 def handlemessage(self, msg: Any) -> Any: 511 # pylint: disable=too-many-branches 512 assert not self.expired 513 514 # Keep track of if we're being held and by who most recently. 515 if isinstance(msg, bs.PickedUpMessage): 516 super().handlemessage(msg) # Augment standard behavior. 517 self.held_count += 1 518 picked_up_by = msg.node.source_player 519 if picked_up_by: 520 self.last_player_held_by = picked_up_by 521 522 elif isinstance(msg, bs.DroppedMessage): 523 super().handlemessage(msg) # Augment standard behavior. 524 self.held_count -= 1 525 if self.held_count < 0: 526 print('ERROR: spaz held_count < 0') 527 528 # Let's count someone dropping us as an attack. 529 try: 530 if msg.node: 531 picked_up_by = msg.node.source_player 532 else: 533 picked_up_by = None 534 except Exception: 535 logging.exception('Error on SpazBot DroppedMessage.') 536 picked_up_by = None 537 538 if picked_up_by: 539 self.last_player_attacked_by = picked_up_by 540 self.last_attacked_time = bs.time() 541 self.last_attacked_type = ('picked_up', 'default') 542 543 elif isinstance(msg, bs.DieMessage): 544 # Report normal deaths for scoring purposes. 545 if not self._dead and not msg.immediate: 546 killerplayer: bs.Player | None 547 548 # If this guy was being held at the time of death, the 549 # holder is the killer. 550 if self.held_count > 0 and self.last_player_held_by: 551 killerplayer = self.last_player_held_by 552 else: 553 # If they were attacked by someone in the last few 554 # seconds that person's the killer. 555 # Otherwise it was a suicide. 556 if ( 557 self.last_player_attacked_by 558 and bs.time() - self.last_attacked_time < 4.0 559 ): 560 killerplayer = self.last_player_attacked_by 561 else: 562 killerplayer = None 563 activity = self._activity() 564 565 # (convert dead player refs to None) 566 if not killerplayer: 567 killerplayer = None 568 if activity is not None: 569 activity.handlemessage( 570 SpazBotDiedMessage(self, killerplayer, msg.how) 571 ) 572 super().handlemessage(msg) # Augment standard behavior. 573 574 # Keep track of the player who last hit us for point rewarding. 575 elif isinstance(msg, bs.HitMessage): 576 source_player = msg.get_source_player(bs.Player) 577 if source_player: 578 self.last_player_attacked_by = source_player 579 self.last_attacked_time = bs.time() 580 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 581 super().handlemessage(msg) 582 else: 583 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.
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
The map this bot was created on.
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 @override 494 def on_punched(self, damage: int) -> None: 495 """ 496 Method override; sends bs.SpazBotPunchedMessage 497 to the current activity. 498 """ 499 bs.getactivity().handlemessage(SpazBotPunchedMessage(self, damage))
Method override; sends bs.SpazBotPunchedMessage to the current activity.
501 @override 502 def on_expire(self) -> None: 503 super().on_expire() 504 505 # We're being torn down; release our callback(s) so there's 506 # no chance of them keeping activities or other things alive. 507 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.
509 @override 510 def handlemessage(self, msg: Any) -> Any: 511 # pylint: disable=too-many-branches 512 assert not self.expired 513 514 # Keep track of if we're being held and by who most recently. 515 if isinstance(msg, bs.PickedUpMessage): 516 super().handlemessage(msg) # Augment standard behavior. 517 self.held_count += 1 518 picked_up_by = msg.node.source_player 519 if picked_up_by: 520 self.last_player_held_by = picked_up_by 521 522 elif isinstance(msg, bs.DroppedMessage): 523 super().handlemessage(msg) # Augment standard behavior. 524 self.held_count -= 1 525 if self.held_count < 0: 526 print('ERROR: spaz held_count < 0') 527 528 # Let's count someone dropping us as an attack. 529 try: 530 if msg.node: 531 picked_up_by = msg.node.source_player 532 else: 533 picked_up_by = None 534 except Exception: 535 logging.exception('Error on SpazBot DroppedMessage.') 536 picked_up_by = None 537 538 if picked_up_by: 539 self.last_player_attacked_by = picked_up_by 540 self.last_attacked_time = bs.time() 541 self.last_attacked_type = ('picked_up', 'default') 542 543 elif isinstance(msg, bs.DieMessage): 544 # Report normal deaths for scoring purposes. 545 if not self._dead and not msg.immediate: 546 killerplayer: bs.Player | None 547 548 # If this guy was being held at the time of death, the 549 # holder is the killer. 550 if self.held_count > 0 and self.last_player_held_by: 551 killerplayer = self.last_player_held_by 552 else: 553 # If they were attacked by someone in the last few 554 # seconds that person's the killer. 555 # Otherwise it was a suicide. 556 if ( 557 self.last_player_attacked_by 558 and bs.time() - self.last_attacked_time < 4.0 559 ): 560 killerplayer = self.last_player_attacked_by 561 else: 562 killerplayer = None 563 activity = self._activity() 564 565 # (convert dead player refs to None) 566 if not killerplayer: 567 killerplayer = None 568 if activity is not None: 569 activity.handlemessage( 570 SpazBotDiedMessage(self, killerplayer, msg.how) 571 ) 572 super().handlemessage(msg) # Augment standard behavior. 573 574 # Keep track of the player who last hit us for point rewarding. 575 elif isinstance(msg, bs.HitMessage): 576 source_player = msg.get_source_player(bs.Player) 577 if source_player: 578 self.last_player_attacked_by = source_player 579 self.last_attacked_time = bs.time() 580 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 581 super().handlemessage(msg) 582 else: 583 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
- default_hitpoints
- 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
586class BomberBot(SpazBot): 587 """A bot that throws regular bombs and occasionally punches. 588 589 category: Bot Classes 590 """ 591 592 character = 'Spaz' 593 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
- default_hitpoints
- 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
596class BomberBotLite(BomberBot): 597 """A less aggressive yellow version of bs.BomberBot. 598 599 category: Bot Classes 600 """ 601 602 color = LITE_BOT_COLOR 603 highlight = LITE_BOT_HIGHLIGHT 604 punchiness = 0.2 605 throw_rate = 0.7 606 throwiness = 0.1 607 charge_speed_min = 0.6 608 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
- default_hitpoints
- 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
611class BomberBotStaticLite(BomberBotLite): 612 """A less aggressive generally immobile weak version of bs.BomberBot. 613 614 category: Bot Classes 615 """ 616 617 static = True 618 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
- default_hitpoints
- 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
621class BomberBotStatic(BomberBot): 622 """A version of bs.BomberBot who generally stays in one place. 623 624 category: Bot Classes 625 """ 626 627 static = True 628 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
- default_hitpoints
- 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
631class BomberBotPro(BomberBot): 632 """A more powerful version of bs.BomberBot. 633 634 category: Bot Classes 635 """ 636 637 points_mult = 2 638 color = PRO_BOT_COLOR 639 highlight = PRO_BOT_HIGHLIGHT 640 default_bomb_count = 3 641 default_boxing_gloves = True 642 punchiness = 0.7 643 throw_rate = 1.3 644 run = True 645 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
- default_hitpoints
- 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
648class BomberBotProShielded(BomberBotPro): 649 """A more powerful version of bs.BomberBot who starts with shields. 650 651 category: Bot Classes 652 """ 653 654 points_mult = 3 655 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
- default_hitpoints
- 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
658class BomberBotProStatic(BomberBotPro): 659 """A more powerful bs.BomberBot who generally stays in one place. 660 661 category: Bot Classes 662 """ 663 664 static = True 665 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
- default_hitpoints
- 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
668class BomberBotProStaticShielded(BomberBotProShielded): 669 """A powerful bs.BomberBot with shields who is generally immobile. 670 671 category: Bot Classes 672 """ 673 674 static = True 675 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
- default_hitpoints
- 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
678class BrawlerBot(SpazBot): 679 """A bot who walks and punches things. 680 681 category: Bot Classes 682 """ 683 684 character = 'Kronk' 685 punchiness = 0.9 686 charge_dist_max = 9999.0 687 charge_speed_min = 1.0 688 charge_speed_max = 1.0 689 throw_dist_min = 9999 690 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
- default_hitpoints
- 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
693class BrawlerBotLite(BrawlerBot): 694 """A weaker version of bs.BrawlerBot. 695 696 category: Bot Classes 697 """ 698 699 color = LITE_BOT_COLOR 700 highlight = LITE_BOT_HIGHLIGHT 701 punchiness = 0.3 702 charge_speed_min = 0.6 703 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
- default_hitpoints
- 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
706class BrawlerBotPro(BrawlerBot): 707 """A stronger version of bs.BrawlerBot. 708 709 category: Bot Classes 710 """ 711 712 color = PRO_BOT_COLOR 713 highlight = PRO_BOT_HIGHLIGHT 714 run = True 715 run_dist_min = 4.0 716 default_boxing_gloves = True 717 punchiness = 0.95 718 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
- default_hitpoints
- 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
721class BrawlerBotProShielded(BrawlerBotPro): 722 """A stronger version of bs.BrawlerBot who starts with shields. 723 724 category: Bot Classes 725 """ 726 727 default_shields = True 728 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
- default_hitpoints
- 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
731class ChargerBot(SpazBot): 732 """A speedy melee attack bot. 733 734 category: Bot Classes 735 """ 736 737 character = 'Snake Shadow' 738 punchiness = 1.0 739 run = True 740 charge_dist_min = 10.0 741 charge_dist_max = 9999.0 742 charge_speed_min = 1.0 743 charge_speed_max = 1.0 744 throw_dist_min = 9999 745 throw_dist_max = 9999 746 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
- default_hitpoints
- 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
749class BouncyBot(SpazBot): 750 """A speedy attacking melee bot that jumps constantly. 751 752 category: Bot Classes 753 """ 754 755 color = (1, 1, 1) 756 highlight = (1.0, 0.5, 0.5) 757 character = 'Easter Bunny' 758 punchiness = 1.0 759 run = True 760 bouncy = True 761 default_boxing_gloves = True 762 charge_dist_min = 10.0 763 charge_dist_max = 9999.0 764 charge_speed_min = 1.0 765 charge_speed_max = 1.0 766 throw_dist_min = 9999 767 throw_dist_max = 9999 768 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
- default_hitpoints
- 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
771class ChargerBotPro(ChargerBot): 772 """A stronger bs.ChargerBot. 773 774 category: Bot Classes 775 """ 776 777 color = PRO_BOT_COLOR 778 highlight = PRO_BOT_HIGHLIGHT 779 default_boxing_gloves = True 780 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
- default_shields
- default_hitpoints
- 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
783class ChargerBotProShielded(ChargerBotPro): 784 """A stronger bs.ChargerBot who starts with shields. 785 786 category: Bot Classes 787 """ 788 789 default_shields = True 790 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
- default_hitpoints
- 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
793class TriggerBot(SpazBot): 794 """A slow moving bot with trigger bombs. 795 796 category: Bot Classes 797 """ 798 799 character = 'Zoe' 800 punchiness = 0.75 801 throwiness = 0.7 802 charge_dist_max = 1.0 803 charge_speed_min = 0.3 804 charge_speed_max = 0.5 805 throw_dist_min = 3.5 806 throw_dist_max = 5.5 807 default_bomb_type = 'impact' 808 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
- default_hitpoints
- 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
811class TriggerBotStatic(TriggerBot): 812 """A bs.TriggerBot who generally stays in one place. 813 814 category: Bot Classes 815 """ 816 817 static = True 818 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
- default_hitpoints
- 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
821class TriggerBotPro(TriggerBot): 822 """A stronger version of bs.TriggerBot. 823 824 category: Bot Classes 825 """ 826 827 color = PRO_BOT_COLOR 828 highlight = PRO_BOT_HIGHLIGHT 829 default_bomb_count = 3 830 default_boxing_gloves = True 831 charge_speed_min = 1.0 832 charge_speed_max = 1.0 833 punchiness = 0.9 834 throw_rate = 1.3 835 run = True 836 run_dist_min = 6.0 837 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
- default_hitpoints
- 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
840class TriggerBotProShielded(TriggerBotPro): 841 """A stronger version of bs.TriggerBot who starts with shields. 842 843 category: Bot Classes 844 """ 845 846 default_shields = True 847 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
- default_hitpoints
- 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
850class StickyBot(SpazBot): 851 """A crazy bot who runs and throws sticky bombs. 852 853 category: Bot Classes 854 """ 855 856 character = 'Mel' 857 punchiness = 0.9 858 throwiness = 1.0 859 run = True 860 charge_dist_min = 4.0 861 charge_dist_max = 10.0 862 charge_speed_min = 1.0 863 charge_speed_max = 1.0 864 throw_dist_min = 0.0 865 throw_dist_max = 4.0 866 throw_rate = 2.0 867 default_bomb_type = 'sticky' 868 default_bomb_count = 3 869 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
- default_hitpoints
- 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
872class StickyBotStatic(StickyBot): 873 """A crazy bot who throws sticky-bombs but generally stays in one place. 874 875 category: Bot Classes 876 """ 877 878 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
- default_hitpoints
- 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
881class ExplodeyBot(SpazBot): 882 """A bot who runs and explodes in 5 seconds. 883 884 category: Bot Classes 885 """ 886 887 character = 'Jack Morgan' 888 run = True 889 charge_dist_min = 0.0 890 charge_dist_max = 9999 891 charge_speed_min = 1.0 892 charge_speed_max = 1.0 893 throw_dist_min = 9999 894 throw_dist_max = 9999 895 start_cursed = True 896 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
- default_hitpoints
- 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
899class ExplodeyBotNoTimeLimit(ExplodeyBot): 900 """A bot who runs but does not explode on his own. 901 902 category: Bot Classes 903 """ 904 905 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
- default_hitpoints
- 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
908class ExplodeyBotShielded(ExplodeyBot): 909 """A bs.ExplodeyBot who starts with shields. 910 911 category: Bot Classes 912 """ 913 914 default_shields = True 915 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
- default_hitpoints
- 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
918class SpazBotSet: 919 """A container/controller for one or more bs.SpazBots. 920 921 category: Bot Classes 922 """ 923 924 def __init__(self) -> None: 925 """Create a bot-set.""" 926 927 # We spread our bots out over a few lists so we can update 928 # them in a staggered fashion. 929 self._bot_list_count = 5 930 self._bot_add_list = 0 931 self._bot_update_list = 0 932 self._bot_lists: list[list[SpazBot]] = [ 933 [] for _ in range(self._bot_list_count) 934 ] 935 self._spawn_sound = bs.getsound('spawn') 936 self._spawning_count = 0 937 self._bot_update_timer: bs.Timer | None = None 938 self.start_moving() 939 940 def __del__(self) -> None: 941 self.clear() 942 943 def spawn_bot( 944 self, 945 bot_type: type[SpazBot], 946 pos: Sequence[float], 947 spawn_time: float = 3.0, 948 on_spawn_call: Callable[[SpazBot], Any] | None = None, 949 ) -> None: 950 """Spawn a bot from this set.""" 951 from bascenev1lib.actor.spawner import Spawner 952 953 Spawner( 954 pt=pos, 955 spawn_time=spawn_time, 956 send_spawn_message=False, 957 spawn_callback=bs.Call( 958 self._spawn_bot, bot_type, pos, on_spawn_call 959 ), 960 ) 961 self._spawning_count += 1 962 963 def _spawn_bot( 964 self, 965 bot_type: type[SpazBot], 966 pos: Sequence[float], 967 on_spawn_call: Callable[[SpazBot], Any] | None, 968 ) -> None: 969 spaz = bot_type() 970 self._spawn_sound.play(position=pos) 971 assert spaz.node 972 spaz.node.handlemessage('flash') 973 spaz.node.is_area_of_interest = False 974 spaz.handlemessage(bs.StandMessage(pos, random.uniform(0, 360))) 975 self.add_bot(spaz) 976 self._spawning_count -= 1 977 if on_spawn_call is not None: 978 on_spawn_call(spaz) 979 980 def have_living_bots(self) -> bool: 981 """Return whether any bots in the set are alive or spawning.""" 982 return self._spawning_count > 0 or any( 983 any(b.is_alive() for b in l) for l in self._bot_lists 984 ) 985 986 def get_living_bots(self) -> list[SpazBot]: 987 """Get the living bots in the set.""" 988 bots: list[SpazBot] = [] 989 for botlist in self._bot_lists: 990 for bot in botlist: 991 if bot.is_alive(): 992 bots.append(bot) 993 return bots 994 995 def _update(self) -> None: 996 # Update one of our bot lists each time through. 997 # First off, remove no-longer-existing bots from the list. 998 try: 999 bot_list = self._bot_lists[self._bot_update_list] = [ 1000 b for b in self._bot_lists[self._bot_update_list] if b 1001 ] 1002 except Exception: 1003 bot_list = [] 1004 logging.exception( 1005 'Error updating bot list: %s', 1006 self._bot_lists[self._bot_update_list], 1007 ) 1008 self._bot_update_list = ( 1009 self._bot_update_list + 1 1010 ) % self._bot_list_count 1011 1012 # Update our list of player points for the bots to use. 1013 player_pts = [] 1014 for player in bs.getactivity().players: 1015 assert isinstance(player, bs.Player) 1016 try: 1017 # TODO: could use abstracted player.position here so we 1018 # don't have to assume their actor type, but we have no 1019 # abstracted velocity as of yet. 1020 if player.is_alive(): 1021 assert isinstance(player.actor, Spaz) 1022 assert player.actor.node 1023 player_pts.append( 1024 ( 1025 bs.Vec3(player.actor.node.position), 1026 bs.Vec3(player.actor.node.velocity), 1027 ) 1028 ) 1029 except Exception: 1030 logging.exception('Error on bot-set _update.') 1031 1032 for bot in bot_list: 1033 bot.set_player_points(player_pts) 1034 bot.update_ai() 1035 1036 def clear(self) -> None: 1037 """Immediately clear out any bots in the set.""" 1038 1039 # Don't do this if the activity is shutting down or dead. 1040 activity = bs.getactivity(doraise=False) 1041 if activity is None or activity.expired: 1042 return 1043 1044 for i, bot_list in enumerate(self._bot_lists): 1045 for bot in bot_list: 1046 bot.handlemessage(bs.DieMessage(immediate=True)) 1047 self._bot_lists[i] = [] 1048 1049 def start_moving(self) -> None: 1050 """Start processing bot AI updates so they start doing their thing.""" 1051 self._bot_update_timer = bs.Timer( 1052 0.05, bs.WeakCall(self._update), repeat=True 1053 ) 1054 1055 def stop_moving(self) -> None: 1056 """Tell all bots to stop moving and stops updating their AI. 1057 1058 Useful when players have won and you want the 1059 enemy bots to just stand and look bewildered. 1060 """ 1061 self._bot_update_timer = None 1062 for botlist in self._bot_lists: 1063 for bot in botlist: 1064 if bot.node: 1065 bot.node.move_left_right = 0 1066 bot.node.move_up_down = 0 1067 1068 def celebrate(self, duration: float) -> None: 1069 """Tell all living bots in the set to celebrate momentarily. 1070 1071 Duration is given in seconds. 1072 """ 1073 msg = bs.CelebrateMessage(duration=duration) 1074 for botlist in self._bot_lists: 1075 for bot in botlist: 1076 if bot: 1077 bot.handlemessage(msg) 1078 1079 def final_celebrate(self) -> None: 1080 """Tell all bots in the set to stop what they were doing and celebrate. 1081 1082 Use this when the bots have won a game. 1083 """ 1084 self._bot_update_timer = None 1085 1086 # At this point stop doing anything but jumping and celebrating. 1087 for botlist in self._bot_lists: 1088 for bot in botlist: 1089 if bot: 1090 assert bot.node # (should exist if 'if bot' was True) 1091 bot.node.move_left_right = 0 1092 bot.node.move_up_down = 0 1093 bs.timer( 1094 0.5 * random.random(), 1095 bs.Call(bot.handlemessage, bs.CelebrateMessage()), 1096 ) 1097 jump_duration = random.randrange(400, 500) 1098 j = random.randrange(0, 200) 1099 for _i in range(10): 1100 bot.node.jump_pressed = True 1101 bot.node.jump_pressed = False 1102 j += jump_duration 1103 bs.timer( 1104 random.uniform(0.0, 1.0), 1105 bs.Call(bot.node.handlemessage, 'attack_sound'), 1106 ) 1107 bs.timer( 1108 random.uniform(1.0, 2.0), 1109 bs.Call(bot.node.handlemessage, 'attack_sound'), 1110 ) 1111 bs.timer( 1112 random.uniform(2.0, 3.0), 1113 bs.Call(bot.node.handlemessage, 'attack_sound'), 1114 ) 1115 1116 def add_bot(self, bot: SpazBot) -> None: 1117 """Add a bs.SpazBot instance to the set.""" 1118 self._bot_lists[self._bot_add_list].append(bot) 1119 self._bot_add_list = (self._bot_add_list + 1) % self._bot_list_count
A container/controller for one or more bs.SpazBots.
category: Bot Classes
924 def __init__(self) -> None: 925 """Create a bot-set.""" 926 927 # We spread our bots out over a few lists so we can update 928 # them in a staggered fashion. 929 self._bot_list_count = 5 930 self._bot_add_list = 0 931 self._bot_update_list = 0 932 self._bot_lists: list[list[SpazBot]] = [ 933 [] for _ in range(self._bot_list_count) 934 ] 935 self._spawn_sound = bs.getsound('spawn') 936 self._spawning_count = 0 937 self._bot_update_timer: bs.Timer | None = None 938 self.start_moving()
Create a bot-set.
943 def spawn_bot( 944 self, 945 bot_type: type[SpazBot], 946 pos: Sequence[float], 947 spawn_time: float = 3.0, 948 on_spawn_call: Callable[[SpazBot], Any] | None = None, 949 ) -> None: 950 """Spawn a bot from this set.""" 951 from bascenev1lib.actor.spawner import Spawner 952 953 Spawner( 954 pt=pos, 955 spawn_time=spawn_time, 956 send_spawn_message=False, 957 spawn_callback=bs.Call( 958 self._spawn_bot, bot_type, pos, on_spawn_call 959 ), 960 ) 961 self._spawning_count += 1
Spawn a bot from this set.
980 def have_living_bots(self) -> bool: 981 """Return whether any bots in the set are alive or spawning.""" 982 return self._spawning_count > 0 or any( 983 any(b.is_alive() for b in l) for l in self._bot_lists 984 )
Return whether any bots in the set are alive or spawning.
986 def get_living_bots(self) -> list[SpazBot]: 987 """Get the living bots in the set.""" 988 bots: list[SpazBot] = [] 989 for botlist in self._bot_lists: 990 for bot in botlist: 991 if bot.is_alive(): 992 bots.append(bot) 993 return bots
Get the living bots in the set.
1036 def clear(self) -> None: 1037 """Immediately clear out any bots in the set.""" 1038 1039 # Don't do this if the activity is shutting down or dead. 1040 activity = bs.getactivity(doraise=False) 1041 if activity is None or activity.expired: 1042 return 1043 1044 for i, bot_list in enumerate(self._bot_lists): 1045 for bot in bot_list: 1046 bot.handlemessage(bs.DieMessage(immediate=True)) 1047 self._bot_lists[i] = []
Immediately clear out any bots in the set.
1049 def start_moving(self) -> None: 1050 """Start processing bot AI updates so they start doing their thing.""" 1051 self._bot_update_timer = bs.Timer( 1052 0.05, bs.WeakCall(self._update), repeat=True 1053 )
Start processing bot AI updates so they start doing their thing.
1055 def stop_moving(self) -> None: 1056 """Tell all bots to stop moving and stops updating their AI. 1057 1058 Useful when players have won and you want the 1059 enemy bots to just stand and look bewildered. 1060 """ 1061 self._bot_update_timer = None 1062 for botlist in self._bot_lists: 1063 for bot in botlist: 1064 if bot.node: 1065 bot.node.move_left_right = 0 1066 bot.node.move_up_down = 0
Tell all bots to stop moving and stops updating their AI.
Useful when players have won and you want the enemy bots to just stand and look bewildered.
1068 def celebrate(self, duration: float) -> None: 1069 """Tell all living bots in the set to celebrate momentarily. 1070 1071 Duration is given in seconds. 1072 """ 1073 msg = bs.CelebrateMessage(duration=duration) 1074 for botlist in self._bot_lists: 1075 for bot in botlist: 1076 if bot: 1077 bot.handlemessage(msg)
Tell all living bots in the set to celebrate momentarily.
Duration is given in seconds.
1079 def final_celebrate(self) -> None: 1080 """Tell all bots in the set to stop what they were doing and celebrate. 1081 1082 Use this when the bots have won a game. 1083 """ 1084 self._bot_update_timer = None 1085 1086 # At this point stop doing anything but jumping and celebrating. 1087 for botlist in self._bot_lists: 1088 for bot in botlist: 1089 if bot: 1090 assert bot.node # (should exist if 'if bot' was True) 1091 bot.node.move_left_right = 0 1092 bot.node.move_up_down = 0 1093 bs.timer( 1094 0.5 * random.random(), 1095 bs.Call(bot.handlemessage, bs.CelebrateMessage()), 1096 ) 1097 jump_duration = random.randrange(400, 500) 1098 j = random.randrange(0, 200) 1099 for _i in range(10): 1100 bot.node.jump_pressed = True 1101 bot.node.jump_pressed = False 1102 j += jump_duration 1103 bs.timer( 1104 random.uniform(0.0, 1.0), 1105 bs.Call(bot.node.handlemessage, 'attack_sound'), 1106 ) 1107 bs.timer( 1108 random.uniform(1.0, 2.0), 1109 bs.Call(bot.node.handlemessage, 'attack_sound'), 1110 ) 1111 bs.timer( 1112 random.uniform(2.0, 3.0), 1113 bs.Call(bot.node.handlemessage, 'attack_sound'), 1114 )
Tell all bots in the set to stop what they were doing and celebrate.
Use this when the bots have won a game.