Created
January 10, 2026 15:25
-
-
Save bearlikelion/ad6f7f1e69253dc36687832c16fe8c0c to your computer and use it in GitHub Desktop.
Cooties PR2 player.gd solution
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class_name Player | |
| extends CharacterBody2D | |
| enum { | |
| NULL = 0, | |
| DEFAULT_LAYER = 1, | |
| ONE_WAY_LAYER = 2, | |
| BOUNCY_LAYER = 4 | |
| } | |
| @export var animated_sprite_2d: AnimatedSprite2D | |
| @export var player_name: Label | |
| # Movement configuration | |
| @export var move_speed: float = 200.0 | |
| @export var acceleration: float = 1000.0 | |
| @export var friction: float = 1200.0 | |
| # Jump configuration | |
| @export var jump_velocity: float = -400.0 | |
| @export var double_jump_velocity: float = -350.0 | |
| @export var wall_jump_velocity: Vector2 = Vector2(300.0, -400.0) | |
| # Wall slide configuration | |
| @export var wall_slide_speed: float = 60.0 | |
| # Physics | |
| @export var gravity_scale: float = 1.0 | |
| @export var max_fall_speed: float = 500.0 | |
| # State tracking | |
| var has_double_jump: bool = true | |
| var is_wall_sliding: bool = false | |
| var was_on_floor: bool = false | |
| var is_infected: bool = false | |
| # Bounce tracking | |
| var pending_bounce_velocity: Vector2 = Vector2.ZERO | |
| var has_pending_bounce: bool = false | |
| @onready var infected_particles: GPUParticles2D = $Infected | |
| @onready var wall_check_left: RayCast2D = $WallCheckLeft | |
| @onready var wall_check_right: RayCast2D = $WallCheckRight | |
| @onready var infection_area: Area2D = $InfectionArea | |
| @onready var one_way_check: Area2D = $OneWayCheck | |
| @onready var fart_sound: AudioStreamPlayer = $FartSound | |
| func _ready() -> void: | |
| # Set player name from Global | |
| var peer_id: int = int(name) | |
| if player_name: | |
| player_name.text = Global.get_player_name(peer_id) | |
| if not is_multiplayer_authority(): | |
| physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_ON | |
| # Server-only infection collision detection | |
| if multiplayer.is_server() and infection_area: | |
| infection_area.body_entered.connect(_on_infection_area_body_entered) | |
| func _physics_process(delta: float) -> void: | |
| # Only process input for the player we control | |
| if not is_multiplayer_authority(): | |
| return | |
| # Apply pending bounce from last frame FIRST | |
| if has_pending_bounce: | |
| print("Pending Bounce: %s" % pending_bounce_velocity) | |
| velocity = pending_bounce_velocity | |
| has_pending_bounce = false | |
| elif not is_on_floor(): | |
| # Apply normal gravity | |
| velocity.y = min(velocity.y + get_gravity().y * gravity_scale * delta, max_fall_speed) | |
| # Track floor state for jump resets | |
| if is_on_floor() and not was_on_floor: | |
| _on_landed() | |
| was_on_floor = is_on_floor() | |
| #Handle one-way platforms | |
| _handle_one_way() | |
| # Handle wall sliding | |
| _handle_wall_slide() | |
| # Handle jumping | |
| _handle_jump() | |
| # Handle horizontal movement | |
| _handle_movement(delta) | |
| # Apply movement | |
| _apply_physics() | |
| # Update animations | |
| _update_animation() | |
| func _apply_physics() -> void: | |
| # Store velocity BEFORE move_and_slide modifies it | |
| var vel_before_collision: Vector2 = velocity | |
| move_and_slide() | |
| var col: KinematicCollision2D = get_last_slide_collision() | |
| if not col: | |
| return | |
| # Debug: print what layer we hit | |
| # var layer: int = PhysicsServer2D.body_get_collision_layer(col.get_collider_rid()) | |
| # print("Hit layer: ", layer, " (binary: ", String.num_int64(layer, 2), ")") | |
| # Check if we hit a bouncy platform | |
| if (PhysicsServer2D.body_get_collision_layer(col.get_collider_rid()) & BOUNCY_LAYER): | |
| # Use velocity from BEFORE the collision | |
| pending_bounce_velocity = _bouncy_col_math(vel_before_collision, col.get_normal()) | |
| has_pending_bounce = true | |
| if is_on_floor(): | |
| _on_landed() | |
| # Does the math for a bouncy collision against a static object. | |
| func _bouncy_col_math(vel: Vector2, normal: Vector2) -> Vector2: | |
| # DEBUG | |
| print("_bouncy_col_math inputs:") | |
| print(" vel = ", vel) | |
| print(" normal = ", normal) | |
| # First, project the vel vector onto the normal | |
| # This shortened version of projection math only works cuz we know normal is a unit vector | |
| var proj: Vector2 = normal * vel.dot(normal) | |
| print(" proj = ", proj) # DEBUG | |
| # Then, we take that component and minus it twice off the current velocity. boom. elastic collision against static wall. | |
| var result: Vector2 = vel - 2 * proj | |
| print(" result = ", result) # DEBUG | |
| return result | |
| # Handles the one-way platform functionality. | |
| func _handle_one_way() -> void: | |
| # The only time that a one way platform should have collision is when: its detected by the check, the player is moving downwards or resting, and the down direction is not pressed. | |
| if one_way_check.has_overlapping_bodies() && velocity.y >= 0 && !Input.is_action_pressed("fall_through"): | |
| # We set the collision by modifying our own collision layers. | |
| set_collision_mask_value(ONE_WAY_LAYER,true) | |
| else: | |
| set_collision_mask_value(ONE_WAY_LAYER,false) | |
| # Handles horizontal movement with acceleration and friction | |
| func _handle_movement(delta: float) -> void: | |
| var input_direction: float = Input.get_axis("move_left", "move_right") | |
| # Fallback to UI actions if custom actions don't exist | |
| if input_direction == 0.0: | |
| input_direction = Input.get_axis("ui_left", "ui_right") | |
| if input_direction != 0.0: | |
| # Accelerate towards target speed | |
| velocity.x = move_toward(velocity.x, input_direction * move_speed, acceleration * delta) | |
| # Flip sprite based on direction | |
| animated_sprite_2d.flip_h = input_direction < 0 | |
| else: | |
| # Apply friction when no input | |
| velocity.x = move_toward(velocity.x, 0.0, friction * delta) | |
| # Handles jump, double jump, and wall jump | |
| func _handle_jump() -> void: | |
| var jump_pressed: bool = Input.is_action_just_pressed("jump") or Input.is_action_just_pressed("ui_accept") | |
| if not jump_pressed: | |
| return | |
| # Wall jump | |
| if is_wall_sliding: | |
| _perform_wall_jump() | |
| return | |
| # Normal jump | |
| if is_on_floor(): | |
| velocity.y = jump_velocity | |
| has_double_jump = true | |
| return | |
| # Double jump | |
| if has_double_jump: | |
| velocity.y = double_jump_velocity | |
| has_double_jump = false | |
| # Performs a wall jump | |
| func _perform_wall_jump() -> void: | |
| var wall_normal: float = 1.0 if wall_check_left.is_colliding() else -1.0 | |
| velocity.x = wall_normal * wall_jump_velocity.x | |
| velocity.y = wall_jump_velocity.y | |
| has_double_jump = true | |
| is_wall_sliding = false | |
| # Flip sprite away from wall | |
| animated_sprite_2d.flip_h = wall_normal < 0 | |
| # Handles wall sliding detection and speed | |
| func _handle_wall_slide() -> void: | |
| if is_on_floor(): | |
| is_wall_sliding = false | |
| return | |
| var is_near_wall: bool = wall_check_left.is_colliding() or wall_check_right.is_colliding() | |
| if is_near_wall and velocity.y > 0: | |
| is_wall_sliding = true | |
| velocity.y = min(velocity.y, wall_slide_speed) | |
| has_double_jump = true | |
| else: | |
| is_wall_sliding = false | |
| # Called when player lands on the floor | |
| func _on_landed() -> void: | |
| has_double_jump = true | |
| # Updates the animation based on current state | |
| func _update_animation() -> void: | |
| # Wall slide | |
| if is_wall_sliding: | |
| animated_sprite_2d.play("wall_jump") | |
| return | |
| # Airborne | |
| if not is_on_floor(): | |
| if not has_double_jump: | |
| animated_sprite_2d.play("double_jump") | |
| elif velocity.y < 0: | |
| animated_sprite_2d.play("jump") | |
| else: | |
| animated_sprite_2d.play("fall") | |
| return | |
| # Grounded | |
| if abs(velocity.x) > 10.0: | |
| animated_sprite_2d.play("run") | |
| else: | |
| animated_sprite_2d.play("idle") | |
| # Server-only: Detect infection collision | |
| func _on_infection_area_body_entered(body: Node2D) -> void: | |
| if not multiplayer.is_server() or not is_infected: | |
| return | |
| if body is Player and not body.is_infected: | |
| var game: Game = get_tree().get_first_node_in_group("game") | |
| if game: | |
| game.infect_player(int(body.name)) | |
| # Set infection state and update particle effect | |
| func set_infected(infected_state: bool) -> void: | |
| is_infected = infected_state | |
| if infected_particles: | |
| infected_particles.emitting = infected_state | |
| if is_infected: | |
| fart_sound.play() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment