diff --git a/README.md b/README.md deleted file mode 100644 index 9d281b4..0000000 --- a/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Tokebi Metrics Godot Plugin - -An analytics plugin for Godot games to connect with Tokebi Metrics backend. - -## Features -- Event tracking -- Funnel analysis -- User segmentation - -## Requirements -- Godot version 4.0 or higher -- Tokebi Metrics account [https://tokebimetrics.com] - -## Installation -# Tokebi Godot Plugin — Quick Setup Steps - -1. **Download Installer** - Get the installer script from: - https://tokebimetrics.com/documentation-guide/godot-plugin-guide or use this repo - -2. **Add Installer to Project** - Copy the file `install_tokebi.gd` into the root folder of your Godot project (where your `project.godot` lives). - -3. **Run Installer** - Open `install_tokebi.gd` in Godot’s script editor and run it. This will set up the plugin files and configuration automatically. - -4. **Enable Plugin** - In Godot editor, go to **Project Settings → Plugins**. - Find **Tokebi Analytics SDK** and enable it. - -5. **Configure API Key** - In the new **Tokebi Setup** dock, enter your API key (from your Tokebi dashboard). - -6. **Set AutoLoad** - Go to **Project Settings → AutoLoad**, add the generated `tokebi.gd` script as an autoload singleton named `Tokebi`. - -7. **Start Tracking Events** - In your game scripts, track analytics events with: - - ```gdscript - Tokebi.track("event_name", {"key": "value"}) - - -## License -MIT License. See LICENSE file. - -## Documentation -More detailed docs at — [https://tokebimetrics.com/documentation-guide/godot-plugin-guide] - -## Contact -Created by Marco Diaz — [https://tokebimetrics.com] diff --git a/test/test_enemy.gd b/test/test_enemy.gd index 2bfd969..a20caf3 100644 --- a/test/test_enemy.gd +++ b/test/test_enemy.gd @@ -9,13 +9,16 @@ func before_each(): _mock_parent = PathFollow2D.new() _mock_parent.name = "PathFollow2D_Mock" add_child(_mock_parent) + gut.p(" → PathFollow2D mock created") # Instantiate scene _enemy_instance = EnemyScene.instantiate() _mock_parent.add_child(_enemy_instance) + gut.p(" → Enemy instance instantiated from: %s" % EnemyScene.resource_path) # Initialize child nodes await get_tree().process_frame # Required for complete _ready() execution + gut.p(" → Process frame awaited for initialization") func after_each(): _enemy_instance.queue_free() @@ -23,60 +26,82 @@ func after_each(): # Check for children func test_scene_initialization(): + gut.p("\n▶ TEST: Scene Initialization") var character_body = _enemy_instance.get_node("CharacterBody2D") var health_bar = _enemy_instance.get_node("ProgressBar") + gut.p(" → CharacterBody2D: %s" % ("✓ Found" if character_body else "✗ Missing")) assert_not_null(character_body, "Missing CharacterBody2D") + gut.p(" → ProgressBar: %s" % ("✓ Found" if health_bar else "✗ Missing")) assert_not_null(health_bar, "Missing ProgressBar") # Check damage mechanics func test_shield_damage_reduction(): + gut.p("\n▶ TEST: Shield Damage Reduction") _enemy_instance.current_shield = 50 _enemy_instance.current_health = 100 + gut.p(" → Initial: Shield=%d, Health=%d" % [_enemy_instance.current_shield, _enemy_instance.current_health]) _enemy_instance.enemy_hurt(30) + gut.p(" → After 30 damage: Shield=%d (expected 20), Health=%d (expected 100)" % [_enemy_instance.current_shield, _enemy_instance.current_health]) assert_eq(_enemy_instance.current_shield, 20, "Incorrect shield value") assert_eq(_enemy_instance.current_health, 100, "Health should remain unchanged with this damage amount") func test_shield_break_and_health_damage(): + gut.p("\n▶ TEST: Shield Break and Health Damage") _enemy_instance.current_shield = 20 _enemy_instance.current_health = 100 + gut.p(" → Initial: Shield=%d, Health=%d" % [_enemy_instance.current_shield, _enemy_instance.current_health]) _enemy_instance.enemy_hurt(30) + gut.p(" → After 30 damage: Shield=%d (expected 0), Health=%d (expected 90)" % [_enemy_instance.current_shield, _enemy_instance.current_health]) assert_eq(_enemy_instance.current_shield, 0, "Shield should be depleted") assert_eq(_enemy_instance.current_health, 90, "Incorrect health value") func test_health_damage_without_shield(): + gut.p("\n▶ TEST: Health Damage Without Shield") _enemy_instance.current_shield = 0 _enemy_instance.current_health = 80 + gut.p(" → Initial: Shield=%d, Health=%d" % [_enemy_instance.current_shield, _enemy_instance.current_health]) _enemy_instance.enemy_hurt(25) + gut.p(" → After 25 damage: Shield=%d (expected 0), Health=%d (expected 55)" % [_enemy_instance.current_shield, _enemy_instance.current_health]) assert_eq(_enemy_instance.current_shield, 0, "Shield should remain zero") assert_eq(_enemy_instance.current_health, 55, "Incorrect health amount") #Check for animation change func test_hurt_animation_plays_on_damage(): + gut.p("\n▶ TEST: Hurt Animation on Damage") _enemy_instance.enemy_hurt(10) var anim_sprite = _enemy_instance.get_node("CharacterBody2D/AnimatedSprite2D") + gut.p(" → Animation: %s (expected 'hurt')" % anim_sprite.animation) assert_eq(anim_sprite.animation, "hurt", "Hurt animation must be playing") #Check animation speed change func test_animation_speed_scales_with_speed(): - # Sebesség beállítások ellenőrzése + gut.p("\n▶ TEST: Animation Speed Scaling") _enemy_instance.set_character_data() var anim_sprite = _enemy_instance.get_node("CharacterBody2D/AnimatedSprite2D") var expected_speed = snapped(_enemy_instance.speed / 100.0 , 0.1) - assert_eq(snapped(anim_sprite.speed_scale, 0.1), expected_speed, "Animation speed should match movement speed") + var actual_speed = snapped(anim_sprite.speed_scale, 0.1) + gut.p(" → Character speed: %d" % _enemy_instance.speed) + gut.p(" → Animation speed: %s (expected %s)" % [actual_speed, expected_speed]) + assert_eq(actual_speed, expected_speed, "Animation speed should match movement speed") -#check for cholorchange on healthbar +#check for colorchange on healthbar func test_health_bar_color_changes_with_shield(): + gut.p("\n▶ TEST: Health Bar Color with Shield") _enemy_instance.current_shield = 50 _enemy_instance.adjust_health_bar() var fill_style = _enemy_instance.get_node("ProgressBar").get_theme_stylebox("fill") + gut.p(" → Shield: %d, Color: %s (expected blue)" % [_enemy_instance.current_shield, fill_style.bg_color]) assert_eq(fill_style.bg_color, Color(0,0,1), "Must be blue in case of shield") #Check character flip func test_character_flips_based_on_movement_direction(): + gut.p("\n▶ TEST: Character Flip Direction") var initial_scale = _mock_parent.scale.x _mock_parent.position.x += 10 _enemy_instance.move_character(0.1) + gut.p(" → Moving right: scale=%s (expected > 0)" % _mock_parent.scale.x) assert_gt(_mock_parent.scale.x, 0, "Positive scaling when moving to the right") _mock_parent.position.x -= 20 _enemy_instance.move_character(0.1) + gut.p(" → Moving left: scale=%s (expected < 0)" % _mock_parent.scale.x) assert_lt(_mock_parent.scale.x, 0, "Negative scaling when moving left") diff --git a/test/test_tower.gd b/test/test_tower.gd new file mode 100644 index 0000000..ba09dea --- /dev/null +++ b/test/test_tower.gd @@ -0,0 +1,259 @@ +extends GutTest + +var ArcherTower_scene = preload("res://Game/Towers/archer_tower.tscn") +var tower + +func before_each(): + tower = ArcherTower_scene.instantiate() + add_child(tower) + gut.p("════════════════════════════════════════") + gut.p("Setting up test with fresh tower instance") + gut.p("════════════════════════════════════════") + +func after_each(): + gut.p("─────────────────────────────────────────") + gut.p("Cleaning up test resources...") + + # Clean up all spawned bullets from the test's children + var bullets_to_remove = [] + var bullet_count = 0 + for child in get_children(): + if child.name.begins_with("Arrow") or child.name.begins_with("Magic") or child.name.begins_with("Rock"): + bullets_to_remove.append(child) + bullet_count += 1 + + gut.p("Found %d bullets to remove: %s" % [bullet_count, bullets_to_remove.map(func(b): return b.name)]) + + for bullet in bullets_to_remove: + bullet.queue_free() + + if tower and not tower.is_queued_for_deletion(): + tower.queue_free() + gut.p("Tower cleaned up successfully") + + gut.p("─────────────────────────────────────────\n") + +func test_properties_initialize(): + gut.p("\n▶ TEST: Properties Initialize") + gut.p(" Checking default tower property values...") + + gut.p(" → shootingTime: %s (expected: 0)" % tower.shootingTime) + assert_eq(tower.shootingTime, 0) + + gut.p(" → bulletSpeed: %s (expected: 600)" % tower.bulletSpeed) + assert_eq(tower.bulletSpeed, 600) + + gut.p(" → hitpoint: %s (expected: 25)" % tower.hitpoint) + assert_eq(tower.hitpoint, 25) + + gut.p(" → follower: %s (expected: true)" % tower.follower) + assert_eq(tower.follower, true) + + gut.p(" → target: %s (expected: null)" % tower.target) + assert_eq(tower.target, null) + + gut.p(" ✓ All default properties initialized correctly") + +func test_set_properties_archer_tower(): + gut.p("\n▶ TEST: Set Properties - Archer Tower") + tower.name = "ArcherTower" + gut.p(" Tower name set to: %s" % tower.name) + + tower.set_properties() + gut.p(" set_properties() called") + + var timer_wait = tower.get_node("Timer").wait_time + gut.p(" → Timer wait_time: %s (expected: 0.5 ±0.01)" % timer_wait) + assert_almost_eq(timer_wait, 0.5, 0.01) + + gut.p(" → bulletSpeed: %s (expected: 600)" % tower.bulletSpeed) + assert_eq(tower.bulletSpeed, 600) + + gut.p(" → hitpoint: %s (expected: 25)" % tower.hitpoint) + assert_eq(tower.hitpoint, 25) + + var radius = tower.get_node("CharacterBody2D/Area2D/CollisionShape2D").shape.radius + gut.p(" → Detection radius: %s (expected: 150)" % radius) + assert_eq(radius, 150) + + gut.p(" ✓ Archer tower properties set correctly") + +func test_set_properties_wizard_tower(): + gut.p("\n▶ TEST: Set Properties - Wizard Tower") + tower.name = "WizardTower" + gut.p(" Tower name set to: %s" % tower.name) + + tower.set_properties() + gut.p(" set_properties() called") + + var timer_wait = tower.get_node("Timer").wait_time + gut.p(" → Timer wait_time: %s (expected: 1.0 ±0.01)" % timer_wait) + assert_almost_eq(timer_wait, 1.0, 0.01) + + gut.p(" → bulletSpeed: %s (expected: 450)" % tower.bulletSpeed) + assert_eq(tower.bulletSpeed, 450) + + gut.p(" → hitpoint: %s (expected: 15)" % tower.hitpoint) + assert_eq(tower.hitpoint, 15) + + gut.p(" ✓ Wizard tower properties set correctly") + +func test_set_properties_mortar_tower(): + gut.p("\n▶ TEST: Set Properties - Mortar Tower") + tower.name = "MortarTower" + gut.p(" Tower name set to: %s" % tower.name) + + tower.set_properties() + gut.p(" set_properties() called") + + var timer_wait = tower.get_node("Timer").wait_time + gut.p(" → Timer wait_time: %s (expected: 2.0 ±0.01)" % timer_wait) + assert_almost_eq(timer_wait, 2.0, 0.01) + + gut.p(" → hitpoint: %s (expected: 80)" % tower.hitpoint) + assert_eq(tower.hitpoint, 80) + + gut.p(" ✓ Mortar tower properties set correctly") + +func test_archer_tower_shoots_arrow(): + gut.p("\n▶ TEST: Archer Tower Shoots Arrow") + tower.name = "ArcherTower" + gut.p(" Tower name set to: %s" % tower.name) + + tower._ready() + gut.p(" Tower _ready() called") + + var dummy_target = Node2D.new() + tower.target = dummy_target + gut.p(" Dummy target assigned to tower.target") + + var bullets_before = 0 + for child in tower.get_parent().get_children(): + if child.name.begins_with("Arrow") or child.name.begins_with("Magic") or child.name.begins_with("Rock"): + bullets_before += 1 + gut.p(" Bullets in scene before shoot(): %d" % bullets_before) + + tower.shoot() + gut.p(" shoot() called") + + var found_arrow = false + var arrow_name = "" + for child in tower.get_parent().get_children(): + if child.name.begins_with("Arrow"): + found_arrow = true + arrow_name = child.name + break + + if found_arrow: + gut.p(" ✓ Arrow created successfully: %s" % arrow_name) + else: + gut.p(" ✗ No arrow found after shoot() call") + + assert_true(found_arrow) + dummy_target.queue_free() + gut.p(" ✓ Archer tower successfully shoots arrows") + +func test_wizard_tower_shoots_magic(): + gut.p("\n▶ TEST: Wizard Tower Shoots Magic") + tower.name = "WizardTower" + gut.p(" Tower name set to: %s" % tower.name) + + tower._ready() + gut.p(" Tower _ready() called") + + var dummy_target = Node2D.new() + tower.target = dummy_target + gut.p(" Dummy target assigned to tower.target") + + var bullets_before = 0 + for child in tower.get_parent().get_children(): + if child.name.begins_with("Arrow") or child.name.begins_with("Magic") or child.name.begins_with("Rock"): + bullets_before += 1 + gut.p(" Bullets in scene before shoot(): %d" % bullets_before) + + tower.shoot() + gut.p(" shoot() called") + + var found_magic = false + var magic_name = "" + for child in tower.get_parent().get_children(): + if child.name.begins_with("Magic"): + found_magic = true + magic_name = child.name + break + + if found_magic: + gut.p(" ✓ Magic projectile created successfully: %s" % magic_name) + else: + gut.p(" ✗ No magic projectile found after shoot() call") + + assert_true(found_magic) + dummy_target.queue_free() + gut.p(" ✓ Wizard tower successfully shoots magic") + +func test_mortar_tower_lifter_animates(): + gut.p("\n▶ TEST: Mortar Tower Lifter Animation") + tower.name = "MortarTower" + gut.p(" Tower name set to: %s" % tower.name) + + tower._ready() + gut.p(" Tower _ready() called") + + tower.lifter = false + tower.lifteramount = 0 + gut.p(" Lifter state reset: lifter=%s, lifteramount=%d" % [tower.lifter, tower.lifteramount]) + + var dummy_target = Node2D.new() + tower.target = dummy_target + gut.p(" Dummy target assigned to tower.target") + + gut.p(" Checking initial lifter state...") + assert_false(tower.lifter) + gut.p(" → Initial lifter: false ✓") + + tower.shoot() + gut.p(" shoot() called") + + gut.p(" Checking lifter state after shoot()...") + var lifter_state = tower.lifter + var lifter_amount = tower.lifteramount + gut.p(" → Lifter state after shoot(): %s (expected: true)" % lifter_state) + assert_true(tower.lifter) + + gut.p(" → Lifter amount: %d (expected: 0)" % lifter_amount) + assert_eq(tower.lifteramount, 0) + + dummy_target.queue_free() + gut.p(" ✓ Mortar tower lifter animation triggered correctly") + +func test_no_shoot_without_target(): + gut.p("\n▶ TEST: No Shoot Without Target") + tower.name = "ArcherTower" + gut.p(" Tower name set to: %s" % tower.name) + + tower._ready() + gut.p(" Tower _ready() called") + + var initial_bullet_count = 0 + for child in tower.get_parent().get_children(): + if child.name.begins_with("Arrow") or child.name.begins_with("Magic") or child.name.begins_with("Rock"): + initial_bullet_count += 1 + gut.p(" Initial bullet count in scene: %d" % initial_bullet_count) + + tower.target = null + gut.p(" Tower target set to: null") + gut.p(" Calling shoot() with no target...") + + tower.shoot() + + var final_bullet_count = 0 + for child in tower.get_parent().get_children(): + if child.name.begins_with("Arrow") or child.name.begins_with("Magic") or child.name.begins_with("Rock"): + final_bullet_count += 1 + gut.p(" Final bullet count in scene: %d" % final_bullet_count) + + var new_bullets_created = final_bullet_count - initial_bullet_count + gut.p(" New bullets created: %d (expected: 0)" % new_bullets_created) + + assert_eq(new_bullets_created, 0) + gut.p(" ✓ Tower correctly prevented shooting without target") diff --git a/test/test_tower.gd.uid b/test/test_tower.gd.uid new file mode 100644 index 0000000..20d6414 --- /dev/null +++ b/test/test_tower.gd.uid @@ -0,0 +1 @@ +uid://g32irt61oxi5