class_name Bullet extends Node2D const LIFE_TIME := 0.6 const FADE_TIME := 0.6 var direction : Vector2 var effective_range : float var maximum_range : float var velocity : float var damage : float var color : Color var _start_position : Vector2 var _age := 0.0 var _distance := 0.0 func _init( position : Vector2, direction : Vector2, effective_range : float, maximum_range : float, velocity : float, damage : float, color : Color, ) -> void: _start_position = position self.position = position self.direction = direction self.effective_range = effective_range self.maximum_range = maximum_range self.velocity = velocity self.damage = damage self.color = color func _on_collide(obj: CollisionObject2D, hit_position: Vector2) -> void: # TODO: Add a global game setting to specify whether shooter or server announces successful hit. # For now, server is the most straight-forward. Eventually, support client predictive movement? pass # protected virtual void OnCollide(CollisionObject2D obj, Vector2 hitPosition) # { # // TODO: Add a global game setting to specify whether shooter or server announces successful hit. # // For now, server is the most straight-forward. Eventually, support client predictive movement? # if (!(this.GetGame() is Server) || !(obj.GetNodeOrNull("Sprite2D") is Sprite2D sprite)) return; # var world = this.GetWorld3d(); # var path = world.GetPathTo(sprite); # var color = new Color(Color, (1 + Color.a) / 2); # RPC.Reliable(world.GetPlayersTracking(BlockPos.FromVector(obj.GlobalPosition).ToChunkPos()), # world.SpawnHit, path, hitPosition, color); # if (obj is Player player) { # var rangeFactor = Math.Min(1.0F, (MaximumRange - _distance) / (MaximumRange - EffectiveRange)); # player.Health -= Damage * rangeFactor; # } # // TODO: Also spawn a ghost of the player who was hit so they can see where they got shot? # } func _process(delta: float) -> void: if _age > LIFE_TIME: modulate = Color(modulate, modulate.a - delta / FADE_TIME) if modulate.a <= 0: queue_free() _age += delta func _physics_process(delta: float) -> void: var previous_position := position _distance = min(maximum_range, velocity * _age) position = _start_position + direction * _distance var mask := PhysicsLayer.WORLD | PhysicsLayer.PLAYERS var ray := PhysicsRayQueryParameters2D.create(previous_position, position, mask) var result := get_world_2d().direct_space_state.intersect_ray(ray) if !result.is_empty(): position = result.position as Vector2 _distance = _start_position.distance_to(position) var obj := result.collider as CollisionObject2D if obj: _on_collide(obj, position - obj.global_position) set_physics_process(false) if _distance > maximum_range: set_physics_process(false) queue_redraw() func _draw() -> void: var points: Array[Vector2] var colors: Array[Color] if _distance > 16: points.append(Vector2.ZERO) colors.append(Color(color, color.a * minf(1, 1 - (_distance - effective_range) / (maximum_range - effective_range)))) if _distance > effective_range: points.append(direction * -(_distance - effective_range)) colors.append(color) points.append(direction * -maxf(0.0, _distance - 16)) colors.append(color) points.append(direction * -_distance) colors.append(Color(color, 0.0)) draw_polyline_colors(points, colors, 1.5, true)