Initial commit

main
copygirl 2 weeks ago
commit 6cc5cd4ecc
  1. 21
      README.md
  2. 93
      Resources/heart.gd
  3. 1
      Resources/heart.gd.uid
  4. BIN
      Resources/heart.glb
  5. 37
      Resources/heart.glb.import
  6. 26
      Resources/heart.tscn
  7. BIN
      Resources/hit.ogg
  8. 19
      Resources/hit.ogg.import
  9. 30
      Resources/normal_randomizer.tres
  10. BIN
      Resources/pride/asexual.png
  11. 34
      Resources/pride/asexual.png.import
  12. BIN
      Resources/pride/bisexual.png
  13. 34
      Resources/pride/bisexual.png.import
  14. BIN
      Resources/pride/gay.png
  15. 34
      Resources/pride/gay.png.import
  16. BIN
      Resources/pride/genderfluid.png
  17. 34
      Resources/pride/genderfluid.png.import
  18. BIN
      Resources/pride/intersex.png
  19. 34
      Resources/pride/intersex.png.import
  20. BIN
      Resources/pride/lesbian.png
  21. 34
      Resources/pride/lesbian.png.import
  22. BIN
      Resources/pride/nonbinary.png
  23. 34
      Resources/pride/nonbinary.png.import
  24. BIN
      Resources/pride/pansexual.png
  25. 34
      Resources/pride/pansexual.png.import
  26. BIN
      Resources/pride/transgender.png
  27. 34
      Resources/pride/transgender.png.import
  28. BIN
      Resources/quack/quack1.ogg
  29. 19
      Resources/quack/quack1.ogg.import
  30. BIN
      Resources/quack/quack10.ogg
  31. 19
      Resources/quack/quack10.ogg.import
  32. BIN
      Resources/quack/quack2.ogg
  33. 19
      Resources/quack/quack2.ogg.import
  34. BIN
      Resources/quack/quack3.ogg
  35. 19
      Resources/quack/quack3.ogg.import
  36. BIN
      Resources/quack/quack4.ogg
  37. 19
      Resources/quack/quack4.ogg.import
  38. BIN
      Resources/quack/quack5.ogg
  39. 19
      Resources/quack/quack5.ogg.import
  40. BIN
      Resources/quack/quack6.ogg
  41. 19
      Resources/quack/quack6.ogg.import
  42. BIN
      Resources/quack/quack7.ogg
  43. 19
      Resources/quack/quack7.ogg.import
  44. BIN
      Resources/quack/quack8.ogg
  45. 19
      Resources/quack/quack8.ogg.import
  46. BIN
      Resources/quack/quack9.ogg
  47. 19
      Resources/quack/quack9.ogg.import
  48. BIN
      Resources/squish/squish1.ogg
  49. 19
      Resources/squish/squish1.ogg.import
  50. BIN
      Resources/squish/squish2.ogg
  51. 19
      Resources/squish/squish2.ogg.import
  52. BIN
      Resources/squish/squish3.ogg
  53. 19
      Resources/squish/squish3.ogg.import
  54. BIN
      Resources/squish/squish4.ogg
  55. 19
      Resources/squish/squish4.ogg.import
  56. BIN
      Resources/squish/squish5.ogg
  57. 19
      Resources/squish/squish5.ogg.import
  58. BIN
      Resources/squish/squish6.ogg
  59. 19
      Resources/squish/squish6.ogg.import
  60. BIN
      Resources/squish/squish7.ogg
  61. 19
      Resources/squish/squish7.ogg.import
  62. BIN
      Resources/squish/squish8.ogg
  63. 19
      Resources/squish/squish8.ogg.import
  64. BIN
      Resources/squish/squish9.ogg
  65. 19
      Resources/squish/squish9.ogg.import
  66. 24
      Resources/sticky_randomizer.tres
  67. 168
      copyThrower.gd
  68. 1
      copyThrower.gd.uid
  69. 6
      copyThrower.tscn
  70. 1
      description.txt

@ -0,0 +1,21 @@
# copyThrower
A customized and personalized object thrower mod for [SnekStudio], made
available in case anyone wants to adapt it for their own use, or take some
inspiration.
Since I am not an affiliate with Twitch (by choice), I can't use redeems,
so instead the thrown objects, hearts in this case, are triggered by simply
inserting unicode heart emoji and Twitch pride emotes in one's message.
[SnekStudio]: https://github.com/ExpiredPopsicle/SnekStudio
## Credits
Feel free to consider the source code of this project to be under public domain.
However, note that I've used some some resources from other people that might not be:
- [Pride flag textures](https://www.prideflags.org/) by **Emily Noble** (GPL3)
- ["Tomato Squish"](https://freesound.org/s/684877/) by **ominouswhoosh** (CC0)
- ["Smashing / squishing"](https://freesound.org/s/269485/) by **editor_adp** (CC0)
- ["Quacky Duck Toy SFX.wav"](https://freesound.org/s/366136/) by **DCSFX** (CC BY-NC 3.0)

@ -0,0 +1,93 @@
extends RigidBody3D
## If this heart were to exist for longer than 1 minute for some reason, destroy it.
## The heart should also disappear if it falls low enough.
var lifetime := 60.0 # 1 minute
## If this heart has impacted another object already.
## Prevents repeated impact sounds and sticking.
var impacted := false
## Relative size of the heart. Affects audio pitch.
## Used later to decide how impactful a collision was.
var size := 1.0
## Whether the heart will stick to avatars when colliding.
var sticky := false
## How long the heart will be sticking before falling off again.
@onready var sticky_timer := randf_range(3, 6) # 3 to 6 seconds
# Remember the original parent this node was created under, so if the
# heart sticks to a character, it can return here once it unsticks.
@onready var original_parent: Node = get_parent()
static var stream_normal: AudioStream = load("res://Mods/copyThrower/Resources/normal_randomizer.tres")
static var stream_sticky: AudioStream = load("res://Mods/copyThrower/Resources/sticky_randomizer.tres")
func _ready() -> void:
body_entered.connect(on_body_entered)
# Give it a little bit of random spin.
var random_vector := Vector3(randf() - 0.5, randf() - 0.5, randf() - 0.5).normalized()
angular_velocity = random_vector * randf_range(0, 25) * TAU
angular_damp = 5.0
func set_size(value: float) -> void:
size = value
$Model.scale *= value
$CollisionShape3D.scale *= value
$AudioStreamPlayer3D.pitch_scale = 1 / lerpf(1, value, 0.5)
func set_sticky(value: bool) -> void:
sticky = value
if value: $AudioStreamPlayer3D.stream = stream_sticky
else: $AudioStreamPlayer3D.stream = stream_normal
func set_material(material: StandardMaterial3D) -> void:
$Model/Heart.material_override = material
func _process(delta: float) -> void:
# Kill heart if it ever lives too long.
lifetime -= delta
if lifetime <= 0: queue_free(); return
# Kill heart if it falls too low.
if global_position.y < -10: queue_free(); return
# Unsticky heart after some time.
if get_parent() is CharacterBody3D:
sticky_timer -= delta
if sticky_timer <= 0:
set_physics_active(true)
reparent(original_parent)
func on_body_entered(body: Node) -> void:
if not (body is CharacterBody3D): return
if impacted: return
impacted = true
$AudioStreamPlayer3D.play()
original_parent.on_collide(self, body)
if sticky:
await get_tree().process_frame;
set_physics_active(false)
set_gravity_scale(0.0)
reparent(body)
func set_physics_active(active: bool) -> void:
if active:
sleeping = false
collision_mask = 1
set_gravity_scale(1.0)
else:
sleeping = true
linear_velocity = Vector3.ZERO
angular_velocity = Vector3.ZERO
collision_mask = 0
collision_layer = 0
set_gravity_scale(0.0)

@ -0,0 +1 @@
uid://ba4w1hier1kps

Binary file not shown.

@ -0,0 +1,37 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bryal7hhfonuv"
path="res://.godot/imported/heart.glb-8f2fa658593887ed7614985c9670402c.scn"
[deps]
source_file="res://Mods/copyThrower/Resources/heart.glb"
dest_files=["res://.godot/imported/heart.glb-8f2fa658593887ed7614985c9670402c.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1

@ -0,0 +1,26 @@
[gd_scene load_steps=5 format=3 uid="uid://m7kmum7ygiw2"]
[ext_resource type="Script" uid="uid://ba4w1hier1kps" path="res://Mods/copyThrower/Resources/heart.gd" id="1_h4gh1"]
[ext_resource type="PackedScene" uid="uid://bryal7hhfonuv" path="res://Mods/copyThrower/Resources/heart.glb" id="1_liglp"]
[ext_resource type="AudioStream" uid="uid://jmgp1vmm5mdf" path="res://Mods/copyThrower/Resources/sticky_randomizer.tres" id="3_4n8sw"]
[sub_resource type="CylinderShape3D" id="CylinderShape3D_rfhqb"]
height = 0.04
radius = 0.055
[node name="Heart" type="RigidBody3D"]
contact_monitor = true
max_contacts_reported = 1
script = ExtResource("1_h4gh1")
[node name="Model" parent="." instance=ExtResource("1_liglp")]
transform = Transform3D(0.6, 0, 0, 0, 0.6, 0, 0, 0, 0.6, 0, 0, 0)
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0)
shape = SubResource("CylinderShape3D_rfhqb")
[node name="AudioStreamPlayer3D" type="AudioStreamPlayer3D" parent="."]
stream = ExtResource("3_4n8sw")
attenuation_model = 3
volume_db = -2.0

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bemf7rek7uw0"
path="res://.godot/imported/hit.ogg-75bb5a24498ee15323ed4e8239a38e29.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/hit.ogg"
dest_files=["res://.godot/imported/hit.ogg-75bb5a24498ee15323ed4e8239a38e29.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

@ -0,0 +1,30 @@
[gd_resource type="AudioStreamRandomizer" load_steps=12 format=3 uid="uid://by5roe3uixyln"]
[ext_resource type="AudioStream" uid="uid://bemf7rek7uw0" path="res://Mods/copyThrower/Resources/hit.ogg" id="1_lgv4g"]
[ext_resource type="AudioStream" uid="uid://w0afmnf25amu" path="res://Mods/copyThrower/Resources/quack/quack1.ogg" id="2_1jhxa"]
[ext_resource type="AudioStream" uid="uid://c0aj3lu57ouk3" path="res://Mods/copyThrower/Resources/quack/quack10.ogg" id="3_s6at6"]
[ext_resource type="AudioStream" uid="uid://brt8c36n7plgk" path="res://Mods/copyThrower/Resources/quack/quack2.ogg" id="4_8h5t2"]
[ext_resource type="AudioStream" uid="uid://btiqou5sh01s0" path="res://Mods/copyThrower/Resources/quack/quack3.ogg" id="5_l41b8"]
[ext_resource type="AudioStream" uid="uid://b8rkjdy5808ft" path="res://Mods/copyThrower/Resources/quack/quack4.ogg" id="6_ghljm"]
[ext_resource type="AudioStream" uid="uid://dtsbx2lq2usys" path="res://Mods/copyThrower/Resources/quack/quack5.ogg" id="7_ujw1d"]
[ext_resource type="AudioStream" uid="uid://d11i0wwlnv6i6" path="res://Mods/copyThrower/Resources/quack/quack6.ogg" id="8_qhlsn"]
[ext_resource type="AudioStream" uid="uid://d1sivl5xxc43h" path="res://Mods/copyThrower/Resources/quack/quack7.ogg" id="9_3m6fm"]
[ext_resource type="AudioStream" uid="uid://dxookg0qh7pte" path="res://Mods/copyThrower/Resources/quack/quack8.ogg" id="10_vcq6v"]
[ext_resource type="AudioStream" uid="uid://dcgqtxd8abl75" path="res://Mods/copyThrower/Resources/quack/quack9.ogg" id="11_o5vlg"]
[resource]
playback_mode = 1
random_pitch = 1.1
streams_count = 11
stream_0/stream = ExtResource("1_lgv4g")
stream_0/weight = 200.0
stream_1/stream = ExtResource("2_1jhxa")
stream_2/stream = ExtResource("4_8h5t2")
stream_3/stream = ExtResource("5_l41b8")
stream_4/stream = ExtResource("6_ghljm")
stream_5/stream = ExtResource("7_ujw1d")
stream_6/stream = ExtResource("8_qhlsn")
stream_7/stream = ExtResource("9_3m6fm")
stream_8/stream = ExtResource("10_vcq6v")
stream_9/stream = ExtResource("11_o5vlg")
stream_10/stream = ExtResource("3_s6at6")

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cgdomumia1234"
path="res://.godot/imported/asexual.png-2b5061eec087826d6fef7abae37e6c5a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/asexual.png"
dest_files=["res://.godot/imported/asexual.png-2b5061eec087826d6fef7abae37e6c5a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bklvlne08bamb"
path="res://.godot/imported/bisexual.png-a126a4370573f7f9c82c5939f7c08bab.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/bisexual.png"
dest_files=["res://.godot/imported/bisexual.png-a126a4370573f7f9c82c5939f7c08bab.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://yca3b7m7vmyk"
path="res://.godot/imported/gay.png-83ada93434c9987daadf01e2a0e1db4d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/gay.png"
dest_files=["res://.godot/imported/gay.png-83ada93434c9987daadf01e2a0e1db4d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://wpeo2k1e1mj4"
path="res://.godot/imported/genderfluid.png-bfa065efb07f77992ef5b7064b1e7d19.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/genderfluid.png"
dest_files=["res://.godot/imported/genderfluid.png-bfa065efb07f77992ef5b7064b1e7d19.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bulpp4vo0y0hu"
path="res://.godot/imported/intersex.png-5628374e71a834027717a98fabd79fb3.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/intersex.png"
dest_files=["res://.godot/imported/intersex.png-5628374e71a834027717a98fabd79fb3.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://doigfjgxdrwwi"
path="res://.godot/imported/lesbian.png-518664f89a7b91d130c4a2df04cfeca3.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/lesbian.png"
dest_files=["res://.godot/imported/lesbian.png-518664f89a7b91d130c4a2df04cfeca3.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bo5rorx5cqnbv"
path="res://.godot/imported/nonbinary.png-bd21c674d6601f9b0758df429dcc498c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/nonbinary.png"
dest_files=["res://.godot/imported/nonbinary.png-bd21c674d6601f9b0758df429dcc498c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://1fho2xcjjy4j"
path="res://.godot/imported/pansexual.png-60a9a7db650bd350916b81d7adbf85d3.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/pansexual.png"
dest_files=["res://.godot/imported/pansexual.png-60a9a7db650bd350916b81d7adbf85d3.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://p705e363jsns"
path="res://.godot/imported/transgender.png-f2978c64b823749f8ea405018724b9f2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Mods/copyThrower/Resources/pride/transgender.png"
dest_files=["res://.godot/imported/transgender.png-f2978c64b823749f8ea405018724b9f2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://w0afmnf25amu"
path="res://.godot/imported/quack1.ogg-153fb2b3fcffd4281fe990160e4c1965.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack1.ogg"
dest_files=["res://.godot/imported/quack1.ogg-153fb2b3fcffd4281fe990160e4c1965.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://c0aj3lu57ouk3"
path="res://.godot/imported/quack10.ogg-100308789c5bfdb515186fd1b1c0daea.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack10.ogg"
dest_files=["res://.godot/imported/quack10.ogg-100308789c5bfdb515186fd1b1c0daea.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://brt8c36n7plgk"
path="res://.godot/imported/quack2.ogg-60ed43efcec098cefa3c0c2f30fdce9c.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack2.ogg"
dest_files=["res://.godot/imported/quack2.ogg-60ed43efcec098cefa3c0c2f30fdce9c.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://btiqou5sh01s0"
path="res://.godot/imported/quack3.ogg-4d5c07869a86a643202431038ef27c57.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack3.ogg"
dest_files=["res://.godot/imported/quack3.ogg-4d5c07869a86a643202431038ef27c57.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://b8rkjdy5808ft"
path="res://.godot/imported/quack4.ogg-d0f262ca60d0614d7393f469abd8beba.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack4.ogg"
dest_files=["res://.godot/imported/quack4.ogg-d0f262ca60d0614d7393f469abd8beba.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dtsbx2lq2usys"
path="res://.godot/imported/quack5.ogg-126a3875c6b96b3c953774c145fe4271.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack5.ogg"
dest_files=["res://.godot/imported/quack5.ogg-126a3875c6b96b3c953774c145fe4271.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://d11i0wwlnv6i6"
path="res://.godot/imported/quack6.ogg-e1986d5fb29c4f2f9ec9cae0804950d0.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack6.ogg"
dest_files=["res://.godot/imported/quack6.ogg-e1986d5fb29c4f2f9ec9cae0804950d0.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://d1sivl5xxc43h"
path="res://.godot/imported/quack7.ogg-a4e63cdc4af40a6f1153b4d8d8df6481.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack7.ogg"
dest_files=["res://.godot/imported/quack7.ogg-a4e63cdc4af40a6f1153b4d8d8df6481.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dxookg0qh7pte"
path="res://.godot/imported/quack8.ogg-31abed4b2c9207e7a850c25532ef1bfb.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack8.ogg"
dest_files=["res://.godot/imported/quack8.ogg-31abed4b2c9207e7a850c25532ef1bfb.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dcgqtxd8abl75"
path="res://.godot/imported/quack9.ogg-5f77a19642102571a53d5acf2d39c978.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/quack/quack9.ogg"
dest_files=["res://.godot/imported/quack9.ogg-5f77a19642102571a53d5acf2d39c978.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://tasj05d071a7"
path="res://.godot/imported/squish1.ogg-a67addf1f9c0f71b553015471b780443.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish1.ogg"
dest_files=["res://.godot/imported/squish1.ogg-a67addf1f9c0f71b553015471b780443.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bhbmaqqahqgy8"
path="res://.godot/imported/squish2.ogg-a7e1e3895e3b26418e52dc3812d256c9.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish2.ogg"
dest_files=["res://.godot/imported/squish2.ogg-a7e1e3895e3b26418e52dc3812d256c9.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://41u5qh27cj4t"
path="res://.godot/imported/squish3.ogg-ec402bc051f3bfea95e16d8b9f0fbd7e.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish3.ogg"
dest_files=["res://.godot/imported/squish3.ogg-ec402bc051f3bfea95e16d8b9f0fbd7e.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://clqc4a5aalhej"
path="res://.godot/imported/squish4.ogg-bc9d7dd3cdfbfca6141e448a7a05c7ee.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish4.ogg"
dest_files=["res://.godot/imported/squish4.ogg-bc9d7dd3cdfbfca6141e448a7a05c7ee.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dxs6yen5eipvt"
path="res://.godot/imported/squish5.ogg-6f9b48b5ae6dd53f1808b4948c754bfd.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish5.ogg"
dest_files=["res://.godot/imported/squish5.ogg-6f9b48b5ae6dd53f1808b4948c754bfd.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dwsqeq70oshsp"
path="res://.godot/imported/squish6.ogg-f31459211a7dcc62a89f634d10a5875e.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish6.ogg"
dest_files=["res://.godot/imported/squish6.ogg-f31459211a7dcc62a89f634d10a5875e.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://8c48dhwcedio"
path="res://.godot/imported/squish7.ogg-8fa24581fd6f162eedf9dcf5a9fba1da.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish7.ogg"
dest_files=["res://.godot/imported/squish7.ogg-8fa24581fd6f162eedf9dcf5a9fba1da.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://ugenhlaoxrgm"
path="res://.godot/imported/squish8.ogg-f94fb399cf6f5cff59f324d588a276e4.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish8.ogg"
dest_files=["res://.godot/imported/squish8.ogg-f94fb399cf6f5cff59f324d588a276e4.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://5uoe5bopdd8t"
path="res://.godot/imported/squish9.ogg-36b3385a49c14f884d87a23b80fb2468.oggvorbisstr"
[deps]
source_file="res://Mods/copyThrower/Resources/squish/squish9.ogg"
dest_files=["res://.godot/imported/squish9.ogg-36b3385a49c14f884d87a23b80fb2468.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

@ -0,0 +1,24 @@
[gd_resource type="AudioStreamRandomizer" load_steps=10 format=3 uid="uid://jmgp1vmm5mdf"]
[ext_resource type="AudioStream" uid="uid://tasj05d071a7" path="res://Mods/copyThrower/Resources/squish/squish1.ogg" id="1_jleyq"]
[ext_resource type="AudioStream" uid="uid://bhbmaqqahqgy8" path="res://Mods/copyThrower/Resources/squish/squish2.ogg" id="2_kt7v0"]
[ext_resource type="AudioStream" uid="uid://41u5qh27cj4t" path="res://Mods/copyThrower/Resources/squish/squish3.ogg" id="3_yx1ld"]
[ext_resource type="AudioStream" uid="uid://clqc4a5aalhej" path="res://Mods/copyThrower/Resources/squish/squish4.ogg" id="4_i4lv8"]
[ext_resource type="AudioStream" uid="uid://dxs6yen5eipvt" path="res://Mods/copyThrower/Resources/squish/squish5.ogg" id="5_7ddlg"]
[ext_resource type="AudioStream" uid="uid://dwsqeq70oshsp" path="res://Mods/copyThrower/Resources/squish/squish6.ogg" id="6_hwrs4"]
[ext_resource type="AudioStream" uid="uid://8c48dhwcedio" path="res://Mods/copyThrower/Resources/squish/squish7.ogg" id="7_5y8ld"]
[ext_resource type="AudioStream" uid="uid://ugenhlaoxrgm" path="res://Mods/copyThrower/Resources/squish/squish8.ogg" id="8_mhcya"]
[ext_resource type="AudioStream" uid="uid://5uoe5bopdd8t" path="res://Mods/copyThrower/Resources/squish/squish9.ogg" id="9_exsdi"]
[resource]
random_pitch = 1.1
streams_count = 9
stream_0/stream = ExtResource("1_jleyq")
stream_1/stream = ExtResource("2_kt7v0")
stream_2/stream = ExtResource("3_yx1ld")
stream_3/stream = ExtResource("4_i4lv8")
stream_4/stream = ExtResource("5_7ddlg")
stream_5/stream = ExtResource("6_hwrs4")
stream_6/stream = ExtResource("7_5y8ld")
stream_7/stream = ExtResource("8_mhcya")
stream_8/stream = ExtResource("9_exsdi")

@ -0,0 +1,168 @@
extends Mod_Base
# How quickly the hearts get thrown, with randomized delay.
@export var queue_delay_min := 0.04
@export var queue_delay_max := 0.16
# Randomized size variation. (Larger = less likely to stick.)
@export var size_min := 0.8
@export var size_max := 1.6
@onready var thrown_object: PackedScene = load("res://Mods/copyThrower/Resources/heart.tscn")
var body_bump := Vector3.ZERO
var head_bump := Quaternion.IDENTITY
var current_body_bump := Vector3.ZERO
var current_head_bump := Quaternion.IDENTITY
var triggers = {
# First number is chance to stick.
"" : [ 0.15, colored_heart(Color.RED) ],
"🧡" : [ 0.15, colored_heart(Color.ORANGE) ],
"💛" : [ 0.15, colored_heart(Color.YELLOW) ],
"💚" : [ 0.15, colored_heart(Color.GREEN) ],
"🩵" : [ 0.15, colored_heart(Color.AQUA) ],
"💙" : [ 0.15, colored_heart(Color.BLUE) ],
"💜" : [ 0.15, colored_heart(Color.PURPLE) ],
"🩷" : [ 0.15, colored_heart(Color.PINK) ],
"🤎" : [ 0.15, colored_heart(Color.BROWN) ],
"🖤" : [ 0.15, colored_heart(Color(0.1, 0.1, 0.1)) ],
"🩶" : [ 0.15, colored_heart(Color.DARK_GRAY) ],
"🤍" : [ 0.15, colored_heart(Color.WHITE_SMOKE) ],
"" : [ 0.15, colored_heart(Color.RED) ], # Oldschool heart
"<3" : [ 0.15, colored_heart(Color8(145, 70, 255)) ], # Twitch purple heart
"AsexualPride" : [ 0.15, textured_heart("asexual") ],
"BisexualPride" : [ 0.15, textured_heart("bisexual") ],
"GayPride" : [ 0.15, textured_heart("gay") ],
"GenderFluidPride" : [ 0.15, textured_heart("genderfluid") ],
"IntersexPride" : [ 0.15, textured_heart("intersex") ],
"LesbianPride" : [ 0.15, textured_heart("lesbian") ],
"NonbinaryPride" : [ 0.15, textured_heart("nonbinary") ],
"PansexualPride" : [ 0.15, textured_heart("pansexual") ],
"TransgenderPride" : [ 0.15, textured_heart("transgender") ],
}
var queue: Array[RigidBody3D] = []
var queue_delay := 0.0
func handle_channel_chat_message(
_cheerer_username: String,
_cheerer_display_name: String,
message: String,
_bits_count: int,
) -> void:
var matches := []
# Collect all the matching substrings in the `matches` array.
for trigger in triggers:
var sticky: float = triggers[trigger][0]
var material: StandardMaterial3D = triggers[trigger][1]
var from_index := 0
while true:
var found := message.find(trigger, from_index)
if found < 0: break
matches.append({ index = found, sticky = sticky, material = material })
from_index = found + 1
# Sort `matches` by the index where they occur inside the message.
matches.sort_custom(func(a, b): return a.index < b.index)
for match in matches:
var object: RigidBody3D = thrown_object.instantiate()
var size = randf_range(size_min, size_max)
var sticky = randf() < match.sticky
# Make it so > 1.0 size is less likely to be sticky.
if sticky and size > 1: sticky = randf() > (size - 1) / (size_max - 1)
object.set_sticky(sticky)
object.set_size(size)
object.set_material(match.material)
add_autodelete_object(object)
queue.append(object)
func _process(delta: float) -> void:
var skeleton := get_skeleton()
# Apply body and head "bumping" that causes the avatar to "shake" in response to being hit.
if skeleton:
current_body_bump = current_body_bump.slerp(body_bump, 1 - 0.001 ** delta)
current_head_bump = current_head_bump.slerp(head_bump, 1 - 0.001 ** delta)
body_bump = body_bump.slerp(Vector3.ZERO, 1 - 0.01 ** delta)
head_bump = head_bump.slerp(Quaternion.IDENTITY, 1 - 0.01 ** delta)
var base_bone := skeleton.find_bone("Hips")
var base_rest := skeleton.get_bone_global_rest(base_bone)
skeleton.set_bone_global_pose(base_bone, base_rest.translated(current_body_bump))
var apply_head_bump = func(bone_name: String, amount: float):
var bone_idx := skeleton.find_bone(bone_name)
var bone_rot := skeleton.get_bone_pose_rotation(bone_idx)
var new_rot := Quaternion.IDENTITY.slerp(current_head_bump, amount) * bone_rot
skeleton.set_bone_pose_rotation(bone_idx, new_rot)
apply_head_bump.call("Head" , 0.6);
apply_head_bump.call("Neck" , 0.3);
apply_head_bump.call("Chest", 0.1);
if queue.is_empty():
queue_delay = 0
else:
queue_delay -= delta
while queue.size() > 0 and queue_delay <= 0:
var object: RigidBody3D = queue.pop_front()
queue_delay += randf_range(queue_delay_min, queue_delay_max)
# Only return now because we do want to clear the queue even if the skeleton was missing, and the doctor was never heard from again! [pause for comedic effect] Anyway, that's how I lost my medical license.
if not skeleton: return
# Add object early so we can use global_position.
add_child(object)
var random_offset := Vector3(randf_range(-0.06, 0.06), randf_range(0.05, 0.3), 0.0)
var random_velocity := Vector3(randf() - 0.5, randf() - 0.5, randf() - 0.5).normalized() * randf_range(0.0, 0.4)
var head_bone := skeleton.find_bone("Head")
var head_pos := skeleton.get_bone_global_pose(head_bone).origin
var target_pos := skeleton.global_position + head_pos + random_offset
var camera_pos := get_viewport().get_camera_3d().global_position
var pos := camera_pos + Vector3([-0.3, 0.3].pick_random(), -0.4, 0)
var vel := (target_pos - pos) * randf_range(1.0, 2.0) + random_velocity
vel[1] += 9.8 * pos.distance_to(target_pos) / vel.length() / 2
object.global_position = pos
object.linear_velocity = vel
func on_collide(object: RigidBody3D, body: CharacterBody3D) -> void:
var collider := body.get_parent() as BoneAttachment3D
if not collider: return
var pos := object.global_position
var vel := object.linear_velocity
# Hits to the head cause the head (and some parent bones) to rotate.
if collider.bone_name == "Head":
var bone_pos := collider.global_position
var rotation := Quaternion(bone_pos.direction_to(pos), bone_pos.direction_to(pos + vel))
rotation = Quaternion.IDENTITY.slerp(rotation, 0.15 * object.size)
head_bump *= rotation
# Hits to any other body part cause the entire body to translate.
else:
vel.y *= 0.25 # Less vertical influence.
body_bump += vel * object.size * 0.05
static func colored_heart(color: Color) -> StandardMaterial3D:
var material := StandardMaterial3D.new()
material.albedo_color = color
return material
static func textured_heart(pride: String) -> StandardMaterial3D:
var texture: Texture2D = load("res://Mods/copyThrower/Resources/pride/" + pride + ".png")
var material := StandardMaterial3D.new()
material.albedo_texture = texture
return material

@ -0,0 +1 @@
uid://ct4jb6me3rbeh

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://bihbkek3jpsvb"]
[ext_resource type="Script" uid="uid://ct4jb6me3rbeh" path="res://Mods/copyThrower/copyThrower.gd" id="1_ac3lf"]
[node name="copyThrower" type="Node"]
script = ExtResource("1_ac3lf")

@ -0,0 +1 @@
An alternative object thrower hardcoded to throw colored and textured pride hearts in response to unicode heart emoji and Twitch pride emotes being sent in Twitch chat.
Loading…
Cancel
Save