| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- class_name Blocks
- extends Node2D
- ## The core plus rings of rotating blocks around it, generated from the level number.
- const BLAST_SCENE := preload("res://scenes/blast.tscn")
- const SEGMENTS_PER_RING := 10
- const TNT_RADIUS := 80.0
- const TNT_CHAIN_DELAY := 0.12 # seconds between one TNT going off and the next
- var core: Core
- var _rings: Array[Node2D] = []
- var _speeds: Array[float] = []
- var _frozen_time := 0.0
- ## Builds the level's rings. Rings are made thinner when needed so they all fit within
- ## [param max_radius]; the ring count, and so the difficulty, stays the same.
- func build(level: int, max_radius: float) -> void:
- var layout := _layout(level)
- var size := minf(layout.size, (max_radius - Params.CORE_RADIUS) / layout.rings)
- for index in range(layout.rings - 1, -1, -1):
- _add_ring(level, size, index, layout.indestructible_chance)
- core = Core.new()
- add_child(core)
- ## Stops the rings turning for a while.
- func freeze(seconds: float) -> void:
- _frozen_time = maxf(_frozen_time, seconds)
- func has_unbreakable() -> bool:
- for ring in _rings:
- for block: Block in ring.get_children():
- if not block.is_breakable() and block.collision_layer != 0:
- return true
- return false
- ## Damages every breakable segment within [param radius] of [param at] (global position).
- func explode(at: Vector2, radius: float) -> void:
- var blast: Node2D = BLAST_SCENE.instantiate()
- blast.radius = radius
- blast.position = to_local(at)
- add_child(blast)
- var query := PhysicsShapeQueryParameters2D.new()
- var circle := CircleShape2D.new()
- circle.radius = radius
- query.shape = circle
- query.transform = Transform2D(0.0, at)
- query.collision_mask = Params.LAYER_BLOCKS
- for hit in get_world_2d().direct_space_state.intersect_shape(query, 64):
- if hit.collider is Block:
- (hit.collider as Block).damage()
- func _physics_process(delta: float) -> void:
- if _frozen_time > 0.0:
- _frozen_time -= delta
- return
- for i in _rings.size():
- _rings[i].rotation_degrees += _speeds[i] * delta
- func _on_detonated(block: Block) -> void:
- var at := block.center()
- get_tree().create_timer(TNT_CHAIN_DELAY, false, true).timeout.connect(explode.bind(at, TNT_RADIUS))
- ## Ring count, ring thickness and indestructible-block chance for a level. All of them only
- ## ever grow with the level, so no level is easier than the one before it.
- static func _layout(level: int) -> Dictionary:
- return {
- rings = Params.ring_count(level),
- size = Params.RING_SIZE,
- indestructible_chance = Params.indestructible_chance(level),
- }
- ## Picks the kind of one segment. Special kinds unlock every 50 levels; their share of
- ## the segments grows with the level, and the newest kind is twice as likely as the others.
- static func _segment_kind(level: int, indestructible_chance: float) -> Block.Kind:
- if indestructible_chance >= randf():
- return Block.Kind.INDESTRUCTIBLE
- var unlocked: Array[Block.Kind] = []
- for kind: Block.Kind in Block.KINDS:
- var unlock: int = Block.KINDS[kind].unlock
- if unlock > 0 and unlock <= level:
- unlocked.append(kind)
- if unlocked.is_empty() or randf() >= Params.special_segment_share(level):
- return Block.Kind.NORMAL
- var weights: Array[float] = []
- for kind in unlocked:
- weights.append(2.0 if kind == unlocked[-1] else 1.0)
- var roll: float = randf() * weights.reduce(func(sum: float, w: float) -> float: return sum + w, 0.0)
- for i in unlocked.size():
- roll -= weights[i]
- if roll <= 0.0:
- return unlocked[i]
- return unlocked[-1]
- func _add_ring(level: int, size: float, index: int, indestructible_chance: float) -> void:
- var inner := index * size + Params.CORE_RADIUS
- var outer := (index + 1) * size + Params.CORE_RADIUS
- # Evenly spaced segment boundaries, each jittered so segments have uneven lengths.
- var step := TAU / SEGMENTS_PER_RING
- var bounds: Array[float] = []
- for i in SEGMENTS_PER_RING:
- bounds.append(step * i + randf_range(-1.0, 1.0) * 0.45 * step)
- bounds.append(bounds[0] + TAU)
- var ring := Node2D.new()
- for i in SEGMENTS_PER_RING:
- var block := Block.new(inner, outer, bounds[i], bounds[i + 1], _segment_kind(level, indestructible_chance))
- block.detonated.connect(_on_detonated)
- ring.add_child(block)
- add_child(ring)
- _rings.append(ring)
- var max_speed := Params.ring_speed(level)
- _speeds.append(randf_range(-max_speed, max_speed))
|