Skip to content

Commit

Permalink
Merge pull request #2 from campenr/feature/projectile-effects
Browse files Browse the repository at this point in the history
Add ProjectileEffect base class and basic functionality
  • Loading branch information
campenr authored Aug 20, 2024
2 parents 7055eab + 82f3f7f commit f2cd26f
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 9 deletions.
24 changes: 24 additions & 0 deletions projectiles/double_laser.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class_name EffectDoubleLaser
extends ProjectileEffect

var Projectile : PackedScene = preload("res://units/projectile.tscn")

@export var laser_offset = Vector2(0, -50)
const is_double_laser = true

func check_not_double_laser(effect: PackedScene) -> bool:
# TODO:: Filter on type? This implementation is Bad
if "is_double_laser" in effect:
return not effect.is_double_laser

# TODO:: Hacky? but const on the node does not work for packed scenes
return effect.resource_path != "res://projectiles/double_laser.tscn"

func modify_creation(owner: Node, projectile_effects: Array, transform: Transform2D):
var p = Projectile.instantiate()
# TODO:: Stacking double-lasers? would mean we only want to filter 1 out of this list.
var non_recursive_effects = projectile_effects.filter(check_not_double_laser)
var doubled_transform = transform
# TODO:: Do we want to offset the laser, or do we want to angle out of the same point of origin?
doubled_transform.origin += laser_offset
p.spawn(owner, non_recursive_effects, doubled_transform)
6 changes: 6 additions & 0 deletions projectiles/double_laser.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://dgp68vbl25onx"]

[ext_resource type="Script" path="res://projectiles/double_laser.gd" id="1_wqupk"]

[node name="DoubleLaser" type="Node"]
script = ExtResource("1_wqupk")
7 changes: 7 additions & 0 deletions projectiles/fast_laser.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://dj6jn0ia62yba"]

[ext_resource type="Script" path="res://projectiles/projectile_effect.gd" id="1_jlt11"]

[node name="FastLaser" type="Node"]
script = ExtResource("1_jlt11")
speed_modifier = 3
2 changes: 2 additions & 0 deletions projectiles/huge_laser.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class_name HugeLaser
extends ProjectileEffect
7 changes: 7 additions & 0 deletions projectiles/huge_laser.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://i6l7jkbauvmf"]

[ext_resource type="Script" path="res://projectiles/huge_laser.gd" id="1_ouvu5"]

[node name="HugeLaser" type="Node"]
script = ExtResource("1_ouvu5")
scale_modifier = 10
71 changes: 71 additions & 0 deletions projectiles/projectile_effect.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
class_name ProjectileEffect
extends Node
# A ProjectileEffect is a set of post-processors meant to be run on every instance of a Projectile.
# This by itself does nothing but is meant to be extended by various types of projectile effect.
# Each effect will compound on the others. Each function will be called by the Projectile scene,
# in order of Effect Priority
# TODO:: Effect Priority as an enum? (to discourage "priority: 9999999")

# Each modifier is a combination of a double value and a modification type.
# This is to indicate if a modification is additive or multiplicitive.
# The default for all modifiers is MULTIPLICITIVE : 1, ultimately resulting in no effect.
# (To subtract and divide, use negative values and decimals < 1)
enum MODIFICATION_TYPE {
ADD,
MULTIPLY
}
@export var effect_priority = -1
@export var speed_modifier_type = MODIFICATION_TYPE.MULTIPLY
@export var speed_modifier = 1
@export var damage_modifier_type = MODIFICATION_TYPE.MULTIPLY
@export var damage_modifier = 1
@export var scale_modifier_type = MODIFICATION_TYPE.MULTIPLY
@export var scale_modifier = 1


# Modify the logic that adds the projectile as a child to the scene.
# This can be used to change spawn locations, quantities, etc.
func modify_creation(owner: Node, projectile_effects: Array, transform: Transform2D):
pass

func _calculate_vector_modification(
base_value: Vector2,
modifier_value: int,
modifier_type: MODIFICATION_TYPE
):
# TODO:: How does godot mutability work? we do NOT want to modify the original modifier
var local_modifier = base_value
if modifier_type == MODIFICATION_TYPE.MULTIPLY:
local_modifier *= modifier_value
elif modifier_type == MODIFICATION_TYPE.ADD:
local_modifier += modifier_value
return local_modifier

# Modify the physics logic of the projectile. This function will be called
# during the _physics_process.
# Uses exported variables by default, and should only be overridden if you're
# doing something really interesting.
func modify_physics(physics_vector: Vector2) -> Vector2:
return _calculate_vector_modification(physics_vector, speed_modifier, speed_modifier_type)


# Simple changes to the appearance using built-in features.
# By default, modifies the scale of the projectile.
func modify_appearance(scale: Vector2) -> Vector2:
return _calculate_vector_modification(owner.scale, scale_modifier, scale_modifier_type)


# To change the sprite itself, elements of the animation, or otherwise add effects
# Hard changes, rather than additions, will be subject to effect_priority.
func modify_animation():
pass


# Called when the node enters the scene tree for the first time.
func _enter_tree():
var projectile = get_owner()
if projectile:
# Projectile scenes are instantiated before being added to the tree
# This function will effectively be called twice, but some operations
# require the parent node to be present first.
projectile.scale_projectile(modify_appearance(projectile.scale))
6 changes: 6 additions & 0 deletions projectiles/projectile_effect.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://bwxewmel1e8si"]

[ext_resource type="Script" path="res://projectiles/projectile_effect.gd" id="1_eosdr"]

[node name="ProjectileEffect" type="Node"]
script = ExtResource("1_eosdr")
3 changes: 3 additions & 0 deletions units/enemy.gd
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ func _physics_process(delta):

func _on_visible_on_screen_notifier_2d_screen_exited():
queue_free()

func _on_area_entered(area):
queue_free()
4 changes: 3 additions & 1 deletion units/enemy.tscn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[gd_scene load_steps=4 format=3 uid="uid://4fjgeuv7aun7"]
[gd_scene load_steps=4 format=3 uid="uid://bdsjg08y1h6yh"]

[ext_resource type="Script" path="res://units/enemy.gd" id="1_6h8a0"]
[ext_resource type="Texture2D" uid="uid://dqkvsqmwue64p" path="res://units/PNG_Parts&Spriter_Animation/Ship1/Ship1.png" id="1_ndkjl"]
Expand All @@ -13,11 +13,13 @@ script = ExtResource("1_6h8a0")
texture = ExtResource("1_ndkjl")

[node name="Area2D" type="Area2D" parent="."]
collision_mask = 3

[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
position = Vector2(0, -0.75)
shape = SubResource("RectangleShape2D_mvxux")

[node name="VisibleOnScreenNotifier2D" type="VisibleOnScreenNotifier2D" parent="."]

[connection signal="area_entered" from="Area2D" to="." method="_on_area_entered"]
[connection signal="screen_exited" from="VisibleOnScreenNotifier2D" to="." method="_on_visible_on_screen_notifier_2d_screen_exited"]
17 changes: 12 additions & 5 deletions units/player.gd
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,22 @@ const HORIZONTAL_BUFFER = 75

@export var Projectile : PackedScene
@onready var projectile_spawner = $ProjectileSpawner
var DoubleLaserEffect : PackedScene = preload("res://projectiles/double_laser.tscn")
var HugeLaserEffect : PackedScene = preload("res://projectiles/huge_laser.tscn")
var FastLaserEffect : PackedScene = preload("res://projectiles/fast_laser.tscn")

# Called when the node enters the scene tree for the first time.
func _ready():
Projectile = load("res://units/projectile.tscn")

func fire():
print('fired!')
var p = Projectile.instantiate()
# Effects must be instantiated for each projectile.
# Finding a way to manage "which effects we need to instantiate" and
# "Which effects are actually on this projectile" is key
var effects = [DoubleLaserEffect, HugeLaserEffect, FastLaserEffect]
p.spawn(owner, effects, transform)

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta):
Expand All @@ -30,8 +41,4 @@ func _physics_process(delta):
position.x += HORIZONTAL_SPEED * delta

if Input.is_action_just_pressed("fire"):
print('fired!')
var p = Projectile.instantiate()
owner.add_child(p)
p.transform = projectile_spawner.global_transform

fire()
38 changes: 35 additions & 3 deletions units/projectile.gd
Original file line number Diff line number Diff line change
@@ -1,13 +1,45 @@
extends Area2D

const SPEED = 750
var projectile_effects: Array = []


func _physics_process(delta):
position += transform.x * SPEED * delta
func spawn(owner: Node, spawn_with_projectile_effects: Array, parent_transform: Transform2D):
# Execute any spawn modification effects for the projectile
print("spawned!")
#projectile_effects = spawn_with_projectile_effects
var local_transform = parent_transform
for effect in spawn_with_projectile_effects:
var instance = effect.instantiate()
projectile_effects.append(instance)
add_child(instance)
instance.set_owner(self)
instance.modify_creation(owner, spawn_with_projectile_effects, local_transform)
# Spawn the default projectile
# TODO:: Projectile spawn modification may want to prevent the default from spawning
# how do?
owner.add_child(self)
projectile_effects = projectile_effects
transform = local_transform

func scale_projectile(new_scale: Vector2):
$Sprite2D.scale = new_scale
$CollisionShape2D.scale = new_scale
scale = new_scale

func _ready():
if scale != Vector2.ONE:
scale_projectile(scale)

func _physics_process(delta):
var position_modifier = transform.x * SPEED * delta
for effect in projectile_effects:
position_modifier = effect.modify_physics(position_modifier)
set_position(position + position_modifier)

func _on_area_entered(area):
# TODO: apply effects on enemy hit. For now just despawn.
queue_free()
area.owner.queue_free()

func _integrate_forces(delta):
scale = Vector2(100, 100)
1 change: 1 addition & 0 deletions units/projectile.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ radius = 4.0

[node name="Projectile" type="Area2D"]
position = Vector2(68, 1)
collision_layer = 2
script = ExtResource("1_t6pyo")

[node name="Sprite2D" type="Sprite2D" parent="."]
Expand Down

0 comments on commit f2cd26f

Please sign in to comment.