anima-core 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.reek.yml +27 -1
- data/CHANGELOG.md +4 -0
- data/README.md +219 -25
- data/agents/codebase-analyzer.md +88 -0
- data/agents/codebase-pattern-finder.md +83 -0
- data/agents/documentation-researcher.md +59 -0
- data/agents/thoughts-analyzer.md +102 -0
- data/agents/web-search-researcher.md +71 -0
- data/anima-core.gemspec +3 -0
- data/app/channels/session_channel.rb +76 -28
- data/app/jobs/agent_request_job.rb +24 -0
- data/app/jobs/analytical_brain_job.rb +33 -0
- data/app/jobs/count_event_tokens_job.rb +1 -1
- data/app/models/concerns/event/broadcasting.rb +20 -2
- data/app/models/event.rb +1 -1
- data/app/models/goal.rb +91 -0
- data/app/models/session.rb +347 -22
- data/config/application.rb +2 -0
- data/db/migrate/20260314075248_add_subagent_support_to_sessions.rb +6 -0
- data/db/migrate/20260314112417_add_granted_tools_to_sessions.rb +5 -0
- data/db/migrate/20260314140000_add_name_to_sessions.rb +7 -0
- data/db/migrate/20260314150000_add_viewport_event_ids_to_sessions.rb +7 -0
- data/db/migrate/20260315100000_add_active_skills_to_sessions.rb +7 -0
- data/db/migrate/20260315140843_create_goals.rb +16 -0
- data/db/migrate/20260315144837_add_completed_at_to_goals.rb +5 -0
- data/db/migrate/20260315191105_add_active_workflow_to_sessions.rb +5 -0
- data/lib/agent_loop.rb +65 -9
- data/lib/agents/definition.rb +116 -0
- data/lib/agents/registry.rb +106 -0
- data/lib/analytical_brain/runner.rb +276 -0
- data/lib/analytical_brain/tools/activate_skill.rb +52 -0
- data/lib/analytical_brain/tools/deactivate_skill.rb +43 -0
- data/lib/analytical_brain/tools/deactivate_workflow.rb +34 -0
- data/lib/analytical_brain/tools/everything_is_ready.rb +28 -0
- data/lib/analytical_brain/tools/finish_goal.rb +62 -0
- data/lib/analytical_brain/tools/read_workflow.rb +58 -0
- data/lib/analytical_brain/tools/rename_session.rb +63 -0
- data/lib/analytical_brain/tools/set_goal.rb +60 -0
- data/lib/analytical_brain/tools/update_goal.rb +60 -0
- data/lib/analytical_brain.rb +23 -0
- data/lib/anima/cli/mcp/secrets.rb +76 -0
- data/lib/anima/cli/mcp.rb +197 -0
- data/lib/anima/cli.rb +4 -0
- data/lib/anima/installer.rb +168 -0
- data/lib/anima/settings.rb +226 -0
- data/lib/anima/version.rb +1 -1
- data/lib/anima.rb +9 -0
- data/lib/credential_store.rb +103 -0
- data/lib/environment_probe.rb +232 -0
- data/lib/llm/client.rb +29 -10
- data/lib/mcp/client_manager.rb +86 -0
- data/lib/mcp/config.rb +213 -0
- data/lib/mcp/health_check.rb +77 -0
- data/lib/mcp/secrets.rb +73 -0
- data/lib/mcp/stdio_transport.rb +206 -0
- data/lib/providers/anthropic.rb +8 -7
- data/lib/shell_session.rb +11 -10
- data/lib/skills/definition.rb +97 -0
- data/lib/skills/registry.rb +105 -0
- data/lib/tools/edit.rb +3 -4
- data/lib/tools/mcp_tool.rb +114 -0
- data/lib/tools/read.rb +15 -16
- data/lib/tools/registry.rb +14 -12
- data/lib/tools/request_feature.rb +121 -0
- data/lib/tools/return_result.rb +81 -0
- data/lib/tools/spawn_specialist.rb +109 -0
- data/lib/tools/spawn_subagent.rb +111 -0
- data/lib/tools/subagent_prompts.rb +12 -0
- data/lib/tools/web_get.rb +8 -9
- data/lib/tui/app.rb +332 -43
- data/lib/tui/message_store.rb +20 -0
- data/lib/tui/screens/chat.rb +207 -20
- data/lib/workflows/definition.rb +97 -0
- data/lib/workflows/registry.rb +89 -0
- data/skills/activerecord/SKILL.md +255 -0
- data/skills/activerecord/examples/associations/association_extensions.rb +298 -0
- data/skills/activerecord/examples/associations/basic_associations.rb +118 -0
- data/skills/activerecord/examples/associations/counter_caches.rb +215 -0
- data/skills/activerecord/examples/associations/polymorphic_associations.rb +217 -0
- data/skills/activerecord/examples/associations/self_referential.rb +302 -0
- data/skills/activerecord/examples/associations/through_associations.rb +203 -0
- data/skills/activerecord/examples/basics/crud_operations.rb +209 -0
- data/skills/activerecord/examples/basics/dirty_tracking.rb +218 -0
- data/skills/activerecord/examples/basics/inheritance.rb +377 -0
- data/skills/activerecord/examples/basics/type_casting.rb +317 -0
- data/skills/activerecord/examples/callbacks/alternatives_to_callbacks.rb +447 -0
- data/skills/activerecord/examples/callbacks/conditional_callbacks.rb +353 -0
- data/skills/activerecord/examples/callbacks/lifecycle_callbacks.rb +280 -0
- data/skills/activerecord/examples/callbacks/transaction_callbacks.rb +340 -0
- data/skills/activerecord/examples/migrations/indexes_and_constraints.rb +337 -0
- data/skills/activerecord/examples/migrations/reversible_patterns.rb +403 -0
- data/skills/activerecord/examples/migrations/safe_patterns.rb +420 -0
- data/skills/activerecord/examples/migrations/schema_changes.rb +277 -0
- data/skills/activerecord/examples/querying/batch_processing.rb +226 -0
- data/skills/activerecord/examples/querying/eager_loading.rb +259 -0
- data/skills/activerecord/examples/querying/finder_methods.rb +170 -0
- data/skills/activerecord/examples/querying/optimization.rb +275 -0
- data/skills/activerecord/examples/querying/scopes.rb +260 -0
- data/skills/activerecord/examples/validations/built_in_validators.rb +277 -0
- data/skills/activerecord/examples/validations/conditional_validations.rb +288 -0
- data/skills/activerecord/examples/validations/custom_validators.rb +381 -0
- data/skills/activerecord/examples/validations/database_constraints.rb +432 -0
- data/skills/activerecord/examples/validations/validation_contexts.rb +367 -0
- data/skills/activerecord/references/associations.md +709 -0
- data/skills/activerecord/references/basics.md +622 -0
- data/skills/activerecord/references/callbacks.md +738 -0
- data/skills/activerecord/references/migrations.md +657 -0
- data/skills/activerecord/references/querying.md +655 -0
- data/skills/activerecord/references/validations.md +596 -0
- data/skills/dragonruby/SKILL.md +250 -0
- data/skills/dragonruby/examples/audio/audio_events.rb +55 -0
- data/skills/dragonruby/examples/audio/background_music.rb +29 -0
- data/skills/dragonruby/examples/audio/crossfade.rb +51 -0
- data/skills/dragonruby/examples/audio/music_controls.rb +51 -0
- data/skills/dragonruby/examples/audio/sound_effects.rb +30 -0
- data/skills/dragonruby/examples/core/coordinate_system.rb +27 -0
- data/skills/dragonruby/examples/core/hello_world.rb +24 -0
- data/skills/dragonruby/examples/core/labels.rb +22 -0
- data/skills/dragonruby/examples/core/sprites.rb +35 -0
- data/skills/dragonruby/examples/core/state_management.rb +29 -0
- data/skills/dragonruby/examples/distribution/background_pause.rb +42 -0
- data/skills/dragonruby/examples/distribution/build_workflow.sh +26 -0
- data/skills/dragonruby/examples/distribution/cvars_production.txt +16 -0
- data/skills/dragonruby/examples/distribution/game_metadata_hd.txt +23 -0
- data/skills/dragonruby/examples/distribution/game_metadata_minimal.txt +9 -0
- data/skills/dragonruby/examples/distribution/game_metadata_mobile.txt +31 -0
- data/skills/dragonruby/examples/distribution/platform_detection.rb +36 -0
- data/skills/dragonruby/examples/distribution/steam_metadata.txt +19 -0
- data/skills/dragonruby/examples/entities/collision_detection.rb +43 -0
- data/skills/dragonruby/examples/entities/entity_lifecycle.rb +68 -0
- data/skills/dragonruby/examples/entities/entity_storage.rb +38 -0
- data/skills/dragonruby/examples/entities/factory_methods.rb +45 -0
- data/skills/dragonruby/examples/entities/random_spawning.rb +50 -0
- data/skills/dragonruby/examples/game-logic/reset_patterns.rb +98 -0
- data/skills/dragonruby/examples/game-logic/save_load.rb +101 -0
- data/skills/dragonruby/examples/game-logic/scoring.rb +104 -0
- data/skills/dragonruby/examples/game-logic/state_transitions.rb +103 -0
- data/skills/dragonruby/examples/game-logic/timers.rb +87 -0
- data/skills/dragonruby/examples/input/action_triggers.rb +36 -0
- data/skills/dragonruby/examples/input/analog_movement.rb +28 -0
- data/skills/dragonruby/examples/input/controller_input.rb +28 -0
- data/skills/dragonruby/examples/input/directional_input.rb +24 -0
- data/skills/dragonruby/examples/input/keyboard_input.rb +28 -0
- data/skills/dragonruby/examples/input/mouse_click.rb +26 -0
- data/skills/dragonruby/examples/input/movement_with_bounds.rb +22 -0
- data/skills/dragonruby/examples/input/normalized_movement.rb +32 -0
- data/skills/dragonruby/examples/rendering/frame_animation.rb +32 -0
- data/skills/dragonruby/examples/rendering/labels.rb +32 -0
- data/skills/dragonruby/examples/rendering/layering.rb +51 -0
- data/skills/dragonruby/examples/rendering/solids.rb +61 -0
- data/skills/dragonruby/examples/rendering/sprites.rb +33 -0
- data/skills/dragonruby/examples/rendering/spritesheet_animation.rb +39 -0
- data/skills/dragonruby/examples/scenes/case_dispatch.rb +60 -0
- data/skills/dragonruby/examples/scenes/class_based.rb +150 -0
- data/skills/dragonruby/examples/scenes/pause_overlay.rb +100 -0
- data/skills/dragonruby/examples/scenes/safe_transitions.rb +68 -0
- data/skills/dragonruby/examples/scenes/scene_transitions.rb +98 -0
- data/skills/dragonruby/examples/scenes/send_dispatch.rb +88 -0
- data/skills/dragonruby/references/audio.md +396 -0
- data/skills/dragonruby/references/core.md +385 -0
- data/skills/dragonruby/references/distribution.md +434 -0
- data/skills/dragonruby/references/entities.md +516 -0
- data/skills/dragonruby/references/game-logic/persistence.md +386 -0
- data/skills/dragonruby/references/game-logic/state.md +389 -0
- data/skills/dragonruby/references/input.md +414 -0
- data/skills/dragonruby/references/rendering/animation.md +467 -0
- data/skills/dragonruby/references/rendering/primitives.md +403 -0
- data/skills/dragonruby/references/scenes.md +443 -0
- data/skills/draper-decorators/SKILL.md +344 -0
- data/skills/draper-decorators/examples/application_decorator.rb +61 -0
- data/skills/draper-decorators/examples/decorator_spec.rb +253 -0
- data/skills/draper-decorators/examples/model_decorator.rb +152 -0
- data/skills/draper-decorators/references/anti-patterns.md +640 -0
- data/skills/draper-decorators/references/patterns.md +507 -0
- data/skills/draper-decorators/references/testing.md +559 -0
- data/skills/gh-issue.md +182 -0
- data/skills/mcp-server/SKILL.md +177 -0
- data/skills/mcp-server/examples/dynamic_tools.rb +36 -0
- data/skills/mcp-server/examples/file_manager_tool.rb +85 -0
- data/skills/mcp-server/examples/http_client.rb +48 -0
- data/skills/mcp-server/examples/http_server.rb +97 -0
- data/skills/mcp-server/examples/rails_integration.rb +88 -0
- data/skills/mcp-server/examples/stdio_server.rb +108 -0
- data/skills/mcp-server/examples/streaming_client.rb +95 -0
- data/skills/mcp-server/references/gotchas.md +183 -0
- data/skills/mcp-server/references/prompts.md +98 -0
- data/skills/mcp-server/references/resources.md +53 -0
- data/skills/mcp-server/references/server.md +140 -0
- data/skills/mcp-server/references/tools.md +146 -0
- data/skills/mcp-server/references/transport.md +104 -0
- data/skills/ratatui-ruby/SKILL.md +315 -0
- data/skills/ratatui-ruby/references/core-concepts.md +340 -0
- data/skills/ratatui-ruby/references/events.md +387 -0
- data/skills/ratatui-ruby/references/frameworks.md +522 -0
- data/skills/ratatui-ruby/references/layout.md +423 -0
- data/skills/ratatui-ruby/references/styling.md +268 -0
- data/skills/ratatui-ruby/references/testing.md +433 -0
- data/skills/ratatui-ruby/references/widgets.md +532 -0
- data/skills/rspec/SKILL.md +340 -0
- data/skills/rspec/examples/core/basic_structure.rb +69 -0
- data/skills/rspec/examples/core/configuration.rb +126 -0
- data/skills/rspec/examples/core/hooks.rb +126 -0
- data/skills/rspec/examples/core/memoized_helpers.rb +139 -0
- data/skills/rspec/examples/core/metadata_filtering.rb +144 -0
- data/skills/rspec/examples/core/shared_examples.rb +145 -0
- data/skills/rspec/examples/factory_bot/associations.rb +314 -0
- data/skills/rspec/examples/factory_bot/build_strategies.rb +272 -0
- data/skills/rspec/examples/factory_bot/callbacks.rb +320 -0
- data/skills/rspec/examples/factory_bot/custom_construction.rb +328 -0
- data/skills/rspec/examples/factory_bot/factory_definition.rb +191 -0
- data/skills/rspec/examples/factory_bot/inheritance.rb +314 -0
- data/skills/rspec/examples/factory_bot/traits.rb +293 -0
- data/skills/rspec/examples/factory_bot/transients.rb +229 -0
- data/skills/rspec/examples/matchers/change.rb +115 -0
- data/skills/rspec/examples/matchers/collections.rb +154 -0
- data/skills/rspec/examples/matchers/comparisons.rb +79 -0
- data/skills/rspec/examples/matchers/composing.rb +155 -0
- data/skills/rspec/examples/matchers/custom_matchers.rb +197 -0
- data/skills/rspec/examples/matchers/equality.rb +58 -0
- data/skills/rspec/examples/matchers/errors.rb +136 -0
- data/skills/rspec/examples/matchers/output.rb +103 -0
- data/skills/rspec/examples/matchers/predicates.rb +87 -0
- data/skills/rspec/examples/matchers/truthiness.rb +101 -0
- data/skills/rspec/examples/matchers/types.rb +82 -0
- data/skills/rspec/examples/matchers/yield.rb +147 -0
- data/skills/rspec/examples/mocks/any_instance.rb +172 -0
- data/skills/rspec/examples/mocks/argument_matchers.rb +206 -0
- data/skills/rspec/examples/mocks/constants.rb +177 -0
- data/skills/rspec/examples/mocks/doubles.rb +139 -0
- data/skills/rspec/examples/mocks/expectations.rb +137 -0
- data/skills/rspec/examples/mocks/message_chains.rb +173 -0
- data/skills/rspec/examples/mocks/ordering.rb +144 -0
- data/skills/rspec/examples/mocks/receive_counts.rb +181 -0
- data/skills/rspec/examples/mocks/responses.rb +223 -0
- data/skills/rspec/examples/mocks/spies.rb +149 -0
- data/skills/rspec/examples/mocks/stubbing.rb +133 -0
- data/skills/rspec/examples/rails/channels.rb +250 -0
- data/skills/rspec/examples/rails/controller_specs.rb +302 -0
- data/skills/rspec/examples/rails/helper_specs.rb +245 -0
- data/skills/rspec/examples/rails/job_specs.rb +256 -0
- data/skills/rspec/examples/rails/mailer_specs.rb +228 -0
- data/skills/rspec/examples/rails/matchers.rb +374 -0
- data/skills/rspec/examples/rails/model_specs.rb +193 -0
- data/skills/rspec/examples/rails/request_specs.rb +275 -0
- data/skills/rspec/examples/rails/routing_specs.rb +276 -0
- data/skills/rspec/examples/rails/system_specs.rb +294 -0
- data/skills/rspec/examples/rails/transactions.rb +254 -0
- data/skills/rspec/examples/rails/view_specs.rb +252 -0
- data/skills/rspec/references/core.md +816 -0
- data/skills/rspec/references/factory_bot.md +641 -0
- data/skills/rspec/references/matchers.md +516 -0
- data/skills/rspec/references/mocks.md +381 -0
- data/skills/rspec/references/rails.md +528 -0
- data/templates/soul.md +40 -0
- data/workflows/commit.md +45 -0
- data/workflows/create_handoff.md +98 -0
- data/workflows/create_note.md +82 -0
- data/workflows/create_plan.md +457 -0
- data/workflows/decompose_ticket.md +109 -0
- data/workflows/feature.md +91 -0
- data/workflows/implement_plan.md +87 -0
- data/workflows/iterate_plan.md +247 -0
- data/workflows/research_codebase.md +210 -0
- data/workflows/resume_handoff.md +217 -0
- data/workflows/review_pr.md +320 -0
- data/workflows/thoughts_init.md +71 -0
- data/workflows/validate_plan.md +166 -0
- metadata +284 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# State Transitions in DragonRuby
|
|
2
|
+
# Demonstrates scene management and game over detection
|
|
3
|
+
|
|
4
|
+
def tick(args)
|
|
5
|
+
args.state.scene ||= :title
|
|
6
|
+
send("#{args.state.scene}_tick", args)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# === TITLE SCENE ===
|
|
10
|
+
def title_tick(args)
|
|
11
|
+
args.outputs.labels << {
|
|
12
|
+
x: 640, y: 450, text: "MY GAME",
|
|
13
|
+
size_enum: 15, anchor_x: 0.5
|
|
14
|
+
}
|
|
15
|
+
args.outputs.labels << {
|
|
16
|
+
x: 640, y: 350, text: "Press SPACE to start",
|
|
17
|
+
size_enum: 3, anchor_x: 0.5
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if args.inputs.keyboard.key_down.space
|
|
21
|
+
# Initialize game state
|
|
22
|
+
args.state.player = { x: 640, y: 100, health: 3 }
|
|
23
|
+
args.state.score = 0
|
|
24
|
+
args.state.scene = :gameplay
|
|
25
|
+
return # Early return on transition
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# === GAMEPLAY SCENE ===
|
|
30
|
+
def gameplay_tick(args)
|
|
31
|
+
# Initialize
|
|
32
|
+
args.state.player ||= { x: 640, y: 100, health: 3 }
|
|
33
|
+
args.state.score ||= 0
|
|
34
|
+
|
|
35
|
+
# Update
|
|
36
|
+
handle_gameplay_input(args)
|
|
37
|
+
check_game_over(args)
|
|
38
|
+
|
|
39
|
+
# Render
|
|
40
|
+
render_gameplay(args)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def handle_gameplay_input(args)
|
|
44
|
+
player = args.state.player
|
|
45
|
+
player.x -= 5 if args.inputs.left
|
|
46
|
+
player.x += 5 if args.inputs.right
|
|
47
|
+
|
|
48
|
+
# Simulate damage
|
|
49
|
+
if args.inputs.keyboard.key_down.x
|
|
50
|
+
player.health -= 1
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Simulate scoring
|
|
54
|
+
if args.inputs.keyboard.key_down.space
|
|
55
|
+
args.state.score += 10
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def check_game_over(args)
|
|
60
|
+
if args.state.player.health <= 0
|
|
61
|
+
args.state.game_over_at = Kernel.tick_count
|
|
62
|
+
args.state.scene = :game_over
|
|
63
|
+
return # Early return critical!
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def render_gameplay(args)
|
|
68
|
+
# Player
|
|
69
|
+
args.outputs.solids << {
|
|
70
|
+
x: args.state.player.x - 25, y: args.state.player.y,
|
|
71
|
+
w: 50, h: 50, r: 0, g: 200, b: 0
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# HUD
|
|
75
|
+
args.outputs.labels << { x: 40, y: 700, text: "Score: #{args.state.score}" }
|
|
76
|
+
args.outputs.labels << { x: 40, y: 670, text: "Health: #{args.state.player.health}" }
|
|
77
|
+
args.outputs.labels << { x: 40, y: 40, text: "X: Take damage | SPACE: Score" }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# === GAME OVER SCENE ===
|
|
81
|
+
def game_over_tick(args)
|
|
82
|
+
args.outputs.labels << {
|
|
83
|
+
x: 640, y: 450, text: "GAME OVER",
|
|
84
|
+
size_enum: 15, anchor_x: 0.5, r: 255, g: 0, b: 0
|
|
85
|
+
}
|
|
86
|
+
args.outputs.labels << {
|
|
87
|
+
x: 640, y: 380, text: "Final Score: #{args.state.score}",
|
|
88
|
+
size_enum: 5, anchor_x: 0.5
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Grace period before accepting restart (30 frames = 0.5 sec)
|
|
92
|
+
elapsed = Kernel.tick_count - args.state.game_over_at
|
|
93
|
+
if elapsed > 30
|
|
94
|
+
args.outputs.labels << {
|
|
95
|
+
x: 640, y: 300, text: "Press SPACE to restart",
|
|
96
|
+
size_enum: 3, anchor_x: 0.5
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if args.inputs.keyboard.key_down.space
|
|
100
|
+
$gtk.reset
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Timer Patterns in DragonRuby
|
|
2
|
+
# Demonstrates frame-based timing, countdowns, and periodic execution
|
|
3
|
+
|
|
4
|
+
FPS = 60
|
|
5
|
+
|
|
6
|
+
def tick(args)
|
|
7
|
+
defaults(args)
|
|
8
|
+
tick_countdown_timer(args)
|
|
9
|
+
tick_progress_bar(args)
|
|
10
|
+
tick_periodic_spawner(args)
|
|
11
|
+
render(args)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def defaults(args)
|
|
15
|
+
# Countdown timer (30 seconds)
|
|
16
|
+
args.state.countdown ||= 30 * FPS
|
|
17
|
+
|
|
18
|
+
# Progress bar (fills over 5 seconds)
|
|
19
|
+
args.state.progress ||= 0
|
|
20
|
+
args.state.progress_max ||= 5 * FPS
|
|
21
|
+
|
|
22
|
+
# Periodic spawner
|
|
23
|
+
args.state.spawn_rate ||= FPS # Every second
|
|
24
|
+
args.state.spawned_count ||= 0
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Pattern 1: Countdown Timer
|
|
28
|
+
def tick_countdown_timer(args)
|
|
29
|
+
return if args.state.countdown <= 0
|
|
30
|
+
|
|
31
|
+
args.state.countdown -= 1
|
|
32
|
+
|
|
33
|
+
if args.state.countdown <= 0
|
|
34
|
+
args.state.time_up = true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Pattern 2: Progress Bar (fills up)
|
|
39
|
+
def tick_progress_bar(args)
|
|
40
|
+
return if args.state.progress >= args.state.progress_max
|
|
41
|
+
|
|
42
|
+
args.state.progress += 1
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Pattern 3: Periodic Execution with zmod?
|
|
46
|
+
def tick_periodic_spawner(args)
|
|
47
|
+
# Execute every spawn_rate frames
|
|
48
|
+
if Kernel.tick_count.zmod?(args.state.spawn_rate)
|
|
49
|
+
args.state.spawned_count += 1
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render(args)
|
|
54
|
+
# Display countdown as seconds
|
|
55
|
+
seconds_left = (args.state.countdown / FPS).ceil
|
|
56
|
+
args.outputs.labels << {
|
|
57
|
+
x: 640, y: 600, text: "Time: #{seconds_left}s",
|
|
58
|
+
size_enum: 5, anchor_x: 0.5
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Display progress percentage
|
|
62
|
+
progress_pct = (args.state.progress.fdiv(args.state.progress_max) * 100).round
|
|
63
|
+
args.outputs.labels << {
|
|
64
|
+
x: 640, y: 500, text: "Progress: #{progress_pct}%",
|
|
65
|
+
size_enum: 3, anchor_x: 0.5
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Progress bar visual
|
|
69
|
+
bar_width = 400
|
|
70
|
+
filled = (args.state.progress.fdiv(args.state.progress_max) * bar_width).round
|
|
71
|
+
args.outputs.solids << { x: 440, y: 450, w: bar_width, h: 20, r: 100, g: 100, b: 100 }
|
|
72
|
+
args.outputs.solids << { x: 440, y: 450, w: filled, h: 20, r: 0, g: 200, b: 0 }
|
|
73
|
+
|
|
74
|
+
# Spawn counter
|
|
75
|
+
args.outputs.labels << {
|
|
76
|
+
x: 640, y: 400, text: "Spawned: #{args.state.spawned_count}",
|
|
77
|
+
size_enum: 3, anchor_x: 0.5
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Time up message
|
|
81
|
+
if args.state.time_up
|
|
82
|
+
args.outputs.labels << {
|
|
83
|
+
x: 640, y: 300, text: "TIME UP!",
|
|
84
|
+
size_enum: 10, anchor_x: 0.5, r: 255, g: 0, b: 0
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# One-shot actions using key_down pattern
|
|
2
|
+
# Perfect for jump, shoot, interact buttons
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
args.state.player ||= { x: 640, y: 100, dy: 0, on_ground: true }
|
|
6
|
+
args.state.bullets ||= []
|
|
7
|
+
|
|
8
|
+
# Jump: only trigger once per press (not while held)
|
|
9
|
+
if args.inputs.keyboard.key_down.space && args.state.player.on_ground
|
|
10
|
+
args.state.player.dy = 15 # Jump velocity
|
|
11
|
+
args.state.player.on_ground = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Shoot: fire one bullet per keypress
|
|
15
|
+
if args.inputs.keyboard.key_down.f
|
|
16
|
+
args.state.bullets << { x: args.state.player.x, y: args.state.player.y,
|
|
17
|
+
created_at: Kernel.tick_count }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Simple gravity and ground collision
|
|
21
|
+
args.state.player.dy -= 0.5
|
|
22
|
+
args.state.player.y += args.state.player.dy
|
|
23
|
+
|
|
24
|
+
if args.state.player.y <= 100
|
|
25
|
+
args.state.player.y = 100
|
|
26
|
+
args.state.player.dy = 0
|
|
27
|
+
args.state.player.on_ground = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Cleanup old bullets
|
|
31
|
+
args.state.bullets.reject! { |b| b.created_at.elapsed_time > 60 }
|
|
32
|
+
|
|
33
|
+
# Render
|
|
34
|
+
args.outputs.sprites << args.state.player.merge(w: 32, h: 32, path: 'sprites/square/blue.png')
|
|
35
|
+
args.outputs.sprites << args.state.bullets.map { |b| b.merge(w: 8, h: 8, path: 'sprites/square/red.png') }
|
|
36
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Smooth analog movement using percentage values
|
|
2
|
+
# Allows for speed variation based on stick deflection
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
controller = args.inputs.controller_one
|
|
6
|
+
args.state.player ||= { x: 640, y: 360, w: 32, h: 32, dx: 0, dy: 0 }
|
|
7
|
+
|
|
8
|
+
max_speed = 8
|
|
9
|
+
|
|
10
|
+
# Analog stick returns -1.0 to 1.0, multiply by max speed
|
|
11
|
+
# Small tilts = slow movement, full tilt = max speed
|
|
12
|
+
args.state.player.dx = controller.left_analog_x_perc * max_speed
|
|
13
|
+
args.state.player.dy = controller.left_analog_y_perc * max_speed
|
|
14
|
+
|
|
15
|
+
# Apply velocity
|
|
16
|
+
args.state.player.x += args.state.player.dx
|
|
17
|
+
args.state.player.y += args.state.player.dy
|
|
18
|
+
|
|
19
|
+
# Clamp to screen
|
|
20
|
+
args.state.player.x = args.state.player.x.clamp(0, 1280 - args.state.player.w)
|
|
21
|
+
args.state.player.y = args.state.player.y.clamp(0, 720 - args.state.player.h)
|
|
22
|
+
|
|
23
|
+
# Visual speed indicator
|
|
24
|
+
speed = Math.sqrt(args.state.player.dx**2 + args.state.player.dy**2)
|
|
25
|
+
args.outputs.labels << { x: 640, y: 700, text: "Speed: #{speed.to_sf} (tilt stick for variable speed)", size_enum: 5, alignment_enum: 1 }
|
|
26
|
+
|
|
27
|
+
args.outputs.sprites << args.state.player.merge(path: 'sprites/square/blue.png')
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Controller button and analog stick input
|
|
2
|
+
# Access via args.inputs.controller_one (or controller_two, etc.)
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
controller = args.inputs.controller_one
|
|
6
|
+
args.state.player ||= { x: 640, y: 360, w: 32, h: 32 }
|
|
7
|
+
|
|
8
|
+
# Button inputs (key_down, key_held, key_up work like keyboard)
|
|
9
|
+
if controller.key_down.a
|
|
10
|
+
args.outputs.labels << { x: 640, y: 600, text: "A button pressed!", size_enum: 5, alignment_enum: 1 }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# D-pad directions
|
|
14
|
+
if controller.key_held.up
|
|
15
|
+
args.state.player.y += 5
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Analog stick positions: left_analog_x_perc and left_analog_y_perc
|
|
19
|
+
# Returns value from -1.0 to 1.0 (0.0 when centered)
|
|
20
|
+
args.state.player.x += controller.left_analog_x_perc * 8
|
|
21
|
+
args.state.player.y += controller.left_analog_y_perc * 8
|
|
22
|
+
|
|
23
|
+
# Display analog values
|
|
24
|
+
args.outputs.labels << { x: 10, y: 700, text: "Left stick X: #{controller.left_analog_x_perc.to_sf}" }
|
|
25
|
+
args.outputs.labels << { x: 10, y: 670, text: "Left stick Y: #{controller.left_analog_y_perc.to_sf}" }
|
|
26
|
+
|
|
27
|
+
args.outputs.sprites << args.state.player.merge(path: 'sprites/square/blue.png')
|
|
28
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Basic directional movement using args.inputs.up/down/left/right
|
|
2
|
+
# These shortcuts check keyboard arrows, WASD, and controller d-pad
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
# Initialize player on first tick
|
|
6
|
+
args.state.player ||= { x: 640, y: 360, w: 32, h: 32,
|
|
7
|
+
path: 'sprites/square/blue.png' }
|
|
8
|
+
|
|
9
|
+
# Check directional inputs - returns true when pressed or held
|
|
10
|
+
if args.inputs.up
|
|
11
|
+
args.state.player.y += 5
|
|
12
|
+
elsif args.inputs.down
|
|
13
|
+
args.state.player.y -= 5
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
if args.inputs.left
|
|
17
|
+
args.state.player.x -= 5
|
|
18
|
+
elsif args.inputs.right
|
|
19
|
+
args.state.player.x += 5
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Render player
|
|
23
|
+
args.outputs.sprites << args.state.player
|
|
24
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Detecting specific keyboard events: key_down, key_held, key_up
|
|
2
|
+
# key_down: fires once when key is first pressed
|
|
3
|
+
# key_held: fires every frame while key is held
|
|
4
|
+
# key_up: fires once when key is released
|
|
5
|
+
|
|
6
|
+
def tick args
|
|
7
|
+
args.state.bullets ||= []
|
|
8
|
+
|
|
9
|
+
# key_down: fires ONCE per press (good for actions)
|
|
10
|
+
if args.inputs.keyboard.key_down.space
|
|
11
|
+
args.state.bullets << { x: 100, y: 100, created_at: Kernel.tick_count }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# key_held: fires EVERY FRAME while pressed (good for continuous movement)
|
|
15
|
+
if args.inputs.keyboard.key_held.w
|
|
16
|
+
args.outputs.labels << { x: 640, y: 360, text: "W is being held", size_enum: 5, alignment_enum: 1 }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# key_up: fires ONCE when released (good for charging mechanics)
|
|
20
|
+
if args.inputs.keyboard.key_up.escape
|
|
21
|
+
args.state.menu_opened_at = Kernel.tick_count
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Display status
|
|
25
|
+
args.outputs.labels << { x: 10, y: 700, text: "Press SPACE to fire (key_down)" }
|
|
26
|
+
args.outputs.labels << { x: 10, y: 670, text: "Hold W for message (key_held)" }
|
|
27
|
+
args.outputs.labels << { x: 10, y: 640, text: "Press ESC to log release (key_up)" }
|
|
28
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Handling mouse clicks and position
|
|
2
|
+
# args.inputs.mouse.click returns event info when clicked
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
# Check for mouse click this frame
|
|
6
|
+
if args.inputs.mouse.click
|
|
7
|
+
# Store the click event (has x, y, created_at properties)
|
|
8
|
+
args.state.last_click = args.inputs.mouse.click
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Display click information
|
|
12
|
+
if args.state.last_click
|
|
13
|
+
click = args.state.last_click
|
|
14
|
+
|
|
15
|
+
# Access click position
|
|
16
|
+
args.outputs.labels << { x: 640, y: 400, text: "Clicked at: #{click.x}, #{click.y}", size_enum: 5, alignment_enum: 1 }
|
|
17
|
+
|
|
18
|
+
# How many frames ago did it happen?
|
|
19
|
+
args.outputs.labels << { x: 640, y: 350, text: "Clicked #{click.created_at_elapsed} ticks ago", size_enum: 5, alignment_enum: 1 }
|
|
20
|
+
|
|
21
|
+
# Draw a marker at click position
|
|
22
|
+
args.outputs.primitives << { x: click.x - 5, y: click.y - 5, w: 10, h: 10, r: 255, g: 0, b: 0, primitive_marker: :solid }
|
|
23
|
+
else
|
|
24
|
+
args.outputs.labels << { x: 640, y: 360, text: "Click anywhere!", size_enum: 5, alignment_enum: 1 }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Movement with screen boundary clamping using .clamp method
|
|
2
|
+
# Prevents player from moving outside screen bounds
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
args.state.player ||= { x: 640, y: 360, w: 64, h: 64 }
|
|
6
|
+
|
|
7
|
+
speed = 5
|
|
8
|
+
|
|
9
|
+
# Apply movement using unified directional helpers
|
|
10
|
+
args.state.player.x += args.inputs.left_right * speed
|
|
11
|
+
args.state.player.y += args.inputs.up_down * speed
|
|
12
|
+
|
|
13
|
+
# Clamp position to stay within screen bounds
|
|
14
|
+
# args.grid.w is 1280, args.grid.h is 720 by default
|
|
15
|
+
args.state.player.x = args.state.player.x.clamp(0, args.grid.w - args.state.player.w)
|
|
16
|
+
args.state.player.y = args.state.player.y.clamp(0, args.grid.h - args.state.player.h)
|
|
17
|
+
|
|
18
|
+
# Visual feedback at boundaries
|
|
19
|
+
args.outputs.primitives << { x: 0, y: 0, w: args.grid.w, h: args.grid.h, r: 255, g: 0, b: 0, primitive_marker: :border }
|
|
20
|
+
args.outputs.sprites << args.state.player.merge(path: 'sprites/square/blue.png')
|
|
21
|
+
args.outputs.labels << { x: 640, y: 700, text: "Try moving off screen - player is clamped", size_enum: 5, alignment_enum: 1 }
|
|
22
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Normalized diagonal movement using left_right and up_down
|
|
2
|
+
# Prevents faster diagonal movement by normalizing the vector
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
args.state.player ||= { x: 640, y: 360, w: 32, h: 32 }
|
|
6
|
+
|
|
7
|
+
speed = 5
|
|
8
|
+
|
|
9
|
+
# args.inputs.left_right returns -1, 0, or 1
|
|
10
|
+
# args.inputs.up_down returns -1, 0, or 1
|
|
11
|
+
dx = args.inputs.left_right
|
|
12
|
+
dy = args.inputs.up_down
|
|
13
|
+
|
|
14
|
+
# Calculate vector length (diagonal movement without normalization = 1.41x speed)
|
|
15
|
+
length = Math.sqrt(dx * dx + dy * dy)
|
|
16
|
+
|
|
17
|
+
# Normalize: divide by length to get unit vector, then multiply by speed
|
|
18
|
+
if length > 0
|
|
19
|
+
dx = (dx / length) * speed
|
|
20
|
+
dy = (dy / length) * speed
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Apply normalized movement
|
|
24
|
+
args.state.player.x += dx
|
|
25
|
+
args.state.player.y += dy
|
|
26
|
+
|
|
27
|
+
# Show speed comparison
|
|
28
|
+
args.outputs.labels << { x: 640, y: 700, text: "Move diagonally - speed stays constant", size_enum: 5, alignment_enum: 1 }
|
|
29
|
+
args.outputs.labels << { x: 640, y: 670, text: "Current speed: #{Math.sqrt(dx**2 + dy**2).to_sf}", size_enum: 5, alignment_enum: 1 }
|
|
30
|
+
|
|
31
|
+
args.outputs.sprites << args.state.player.merge(path: 'sprites/square/blue.png')
|
|
32
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Frame animation using separate PNG files
|
|
2
|
+
|
|
3
|
+
def tick args
|
|
4
|
+
# Looping animation: 6 frames, 4 ticks each, repeat forever
|
|
5
|
+
looping_frame = 0.frame_index(6, 4, true)
|
|
6
|
+
|
|
7
|
+
args.outputs.sprites << {
|
|
8
|
+
x: 200, y: 400, w: 100, h: 100,
|
|
9
|
+
path: "sprites/dragon_fly_#{looping_frame}.png"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# One-time animation triggered by spacebar
|
|
13
|
+
if args.inputs.keyboard.key_down.space
|
|
14
|
+
args.state.attack_at = Kernel.tick_count
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if args.state.attack_at
|
|
18
|
+
frame = args.state.attack_at.frame_index(6, 4, false)
|
|
19
|
+
frame ||= 0 # Stay on first frame when complete
|
|
20
|
+
|
|
21
|
+
args.outputs.sprites << {
|
|
22
|
+
x: 600, y: 400, w: 100, h: 100,
|
|
23
|
+
path: "sprites/dragon_fly_#{frame}.png"
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
args.outputs.labels << {
|
|
28
|
+
x: 640, y: 100,
|
|
29
|
+
text: "Press SPACE for one-time animation",
|
|
30
|
+
anchor_x: 0.5
|
|
31
|
+
}
|
|
32
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Text rendering with alignment and sizing
|
|
2
|
+
|
|
3
|
+
def tick args
|
|
4
|
+
# Size options
|
|
5
|
+
args.outputs.labels << { x: 640, y: 650, text: "Basic Label" }
|
|
6
|
+
args.outputs.labels << { x: 640, y: 600, text: "Small", size_enum: -1 }
|
|
7
|
+
args.outputs.labels << { x: 640, y: 570, text: "Medium", size_enum: 0 }
|
|
8
|
+
args.outputs.labels << { x: 640, y: 540, text: "Large", size_enum: 1 }
|
|
9
|
+
args.outputs.labels << { x: 640, y: 500, text: "Custom", size_px: 32 }
|
|
10
|
+
|
|
11
|
+
# Alignment with anchors
|
|
12
|
+
args.outputs.labels << { x: 640, y: 450, text: "Left", anchor_x: 0, anchor_y: 0.5 }
|
|
13
|
+
args.outputs.labels << { x: 640, y: 420, text: "Center", anchor_x: 0.5, anchor_y: 0.5 }
|
|
14
|
+
args.outputs.labels << { x: 640, y: 390, text: "Right", anchor_x: 1, anchor_y: 0.5 }
|
|
15
|
+
|
|
16
|
+
# Colors
|
|
17
|
+
args.outputs.labels << { x: 640, y: 340, text: "Red", r: 255, g: 0, b: 0 }
|
|
18
|
+
args.outputs.labels << { x: 640, y: 310, text: "Green", r: 0, g: 255, b: 0 }
|
|
19
|
+
args.outputs.labels << { x: 640, y: 280, text: "Blue", r: 0, g: 0, b: 255 }
|
|
20
|
+
args.outputs.labels << { x: 640, y: 250, text: "Faded", a: 128 }
|
|
21
|
+
|
|
22
|
+
# Custom font
|
|
23
|
+
args.outputs.labels << {
|
|
24
|
+
x: 640, y: 200,
|
|
25
|
+
text: "Custom Font",
|
|
26
|
+
font: "fonts/manaspc.ttf",
|
|
27
|
+
size_px: 24, anchor_x: 0.5
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Reference line
|
|
31
|
+
args.outputs.lines << { x: 640, y: 0, x2: 640, y2: 720, r: 100, g: 100, b: 100 }
|
|
32
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# FIFO render order demonstration using args.outputs.primitives
|
|
2
|
+
# Items render in insertion order - first added = behind, last added = on top
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
# All rendering uses args.outputs.primitives for FIFO control
|
|
6
|
+
# Each primitive renders ON TOP of previously added primitives
|
|
7
|
+
|
|
8
|
+
# 1. Background (rendered first = bottom layer)
|
|
9
|
+
args.outputs.primitives << {
|
|
10
|
+
x: 0, y: 0, w: 1280, h: 720,
|
|
11
|
+
r: 50, g: 50, b: 80,
|
|
12
|
+
primitive_marker: :solid
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
# 2. Ground
|
|
16
|
+
args.outputs.primitives << {
|
|
17
|
+
x: 0, y: 0, w: 1280, h: 200,
|
|
18
|
+
r: 100, g: 150, b: 100,
|
|
19
|
+
primitive_marker: :solid
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# 3. Objects (back to front based on insertion order)
|
|
23
|
+
args.outputs.primitives << { x: 100, y: 150, w: 100, h: 150, path: :solid, r: 34, g: 100, b: 34 }
|
|
24
|
+
args.outputs.primitives << { x: 180, y: 150, w: 100, h: 150, path: :solid, r: 34, g: 139, b: 34 }
|
|
25
|
+
args.outputs.primitives << { x: 260, y: 150, w: 100, h: 150, path: :solid, r: 50, g: 200, b: 50 }
|
|
26
|
+
|
|
27
|
+
# Labels for objects
|
|
28
|
+
args.outputs.primitives << { x: 150, y: 120, text: "Back", anchor_x: 0.5 }
|
|
29
|
+
args.outputs.primitives << { x: 230, y: 120, text: "Middle", anchor_x: 0.5 }
|
|
30
|
+
args.outputs.primitives << { x: 310, y: 120, text: "Front", anchor_x: 0.5 }
|
|
31
|
+
|
|
32
|
+
# 4. UI bar (rendered last = top layer)
|
|
33
|
+
args.outputs.primitives << {
|
|
34
|
+
x: 0, y: 680, w: 1280, h: 40,
|
|
35
|
+
r: 0, g: 0, b: 0, a: 200,
|
|
36
|
+
primitive_marker: :solid
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
args.outputs.primitives << {
|
|
40
|
+
x: 640, y: 700,
|
|
41
|
+
text: "UI Layer - rendered last with primitives, appears on top",
|
|
42
|
+
anchor_x: 0.5, r: 255, g: 255, b: 255
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Explanation
|
|
46
|
+
args.outputs.primitives << {
|
|
47
|
+
x: 640, y: 550,
|
|
48
|
+
text: "FIFO: First In = Behind, Last In = On Top",
|
|
49
|
+
anchor_x: 0.5, size_px: 20
|
|
50
|
+
}
|
|
51
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Filled rectangles and borders using args.outputs.primitives
|
|
2
|
+
# Use primitive_marker: :solid or :border for rectangle types
|
|
3
|
+
|
|
4
|
+
def tick args
|
|
5
|
+
# Background using primitives
|
|
6
|
+
args.outputs.primitives << {
|
|
7
|
+
x: 0, y: 0, w: 1280, h: 720,
|
|
8
|
+
r: 32, g: 32, b: 32,
|
|
9
|
+
primitive_marker: :solid
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# Solids with primitive_marker (recommended for FIFO control)
|
|
13
|
+
args.outputs.primitives << {
|
|
14
|
+
x: 100, y: 500, w: 100, h: 100,
|
|
15
|
+
primitive_marker: :solid
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
args.outputs.primitives << {
|
|
19
|
+
x: 220, y: 500, w: 100, h: 100,
|
|
20
|
+
r: 255, g: 0, b: 0,
|
|
21
|
+
primitive_marker: :solid
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
args.outputs.primitives << {
|
|
25
|
+
x: 340, y: 500, w: 100, h: 100,
|
|
26
|
+
r: 0, g: 255, b: 0, a: 128,
|
|
27
|
+
primitive_marker: :solid
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
args.outputs.primitives << {
|
|
31
|
+
x: 460, y: 500, w: 100, h: 100,
|
|
32
|
+
r: 0, g: 128, b: 255, a: 200,
|
|
33
|
+
primitive_marker: :solid
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# path: :solid for better performance with many rectangles
|
|
37
|
+
# (auto-detected as sprite, no primitive_marker needed)
|
|
38
|
+
args.outputs.primitives << {
|
|
39
|
+
x: 580, y: 500, w: 100, h: 100,
|
|
40
|
+
path: :solid,
|
|
41
|
+
r: 255, g: 255, b: 0
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Borders with primitive_marker
|
|
45
|
+
args.outputs.primitives << {
|
|
46
|
+
x: 100, y: 300, w: 100, h: 100,
|
|
47
|
+
r: 255, g: 255, b: 255,
|
|
48
|
+
primitive_marker: :border
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
args.outputs.primitives << {
|
|
52
|
+
x: 220, y: 300, w: 100, h: 100,
|
|
53
|
+
r: 0, g: 255, b: 255,
|
|
54
|
+
primitive_marker: :border
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Labels explaining the approach
|
|
58
|
+
args.outputs.primitives << { x: 340, y: 640, text: "primitive_marker: :solid", size_px: 18 }
|
|
59
|
+
args.outputs.primitives << { x: 580, y: 640, text: "path: :solid (performance)", size_px: 18 }
|
|
60
|
+
args.outputs.primitives << { x: 160, y: 440, text: "primitive_marker: :border", size_px: 18 }
|
|
61
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Image rendering with anchors, rotation, and color tinting
|
|
2
|
+
|
|
3
|
+
def tick args
|
|
4
|
+
# Basic sprite
|
|
5
|
+
args.outputs.sprites << {
|
|
6
|
+
x: 100, y: 500, w: 128, h: 101,
|
|
7
|
+
path: 'dragonruby.png'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
# Centered with anchors
|
|
11
|
+
args.outputs.sprites << {
|
|
12
|
+
x: 400, y: 500, w: 128, h: 101,
|
|
13
|
+
path: 'dragonruby.png',
|
|
14
|
+
anchor_x: 0.5, anchor_y: 0.5
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Rotation around center
|
|
18
|
+
args.outputs.sprites << {
|
|
19
|
+
x: 700, y: 500, w: 128, h: 101,
|
|
20
|
+
path: 'dragonruby.png',
|
|
21
|
+
angle: Kernel.tick_count % 360,
|
|
22
|
+
angle_anchor_x: 0.5, angle_anchor_y: 0.5
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Color tinting, transparency, flipping
|
|
26
|
+
args.outputs.sprites << {
|
|
27
|
+
x: 1000, y: 500, w: 128, h: 101,
|
|
28
|
+
path: 'dragonruby.png',
|
|
29
|
+
r: 255, g: 128, b: 128,
|
|
30
|
+
a: Kernel.tick_count % 255,
|
|
31
|
+
flip_horizontally: true
|
|
32
|
+
}
|
|
33
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Spritesheet animation using tile_x and tile_y
|
|
2
|
+
|
|
3
|
+
def tick args
|
|
4
|
+
args.state.player ||= { x: 640, y: 360, w: 64, h: 64 }
|
|
5
|
+
|
|
6
|
+
moving = args.inputs.left || args.inputs.right
|
|
7
|
+
|
|
8
|
+
if moving
|
|
9
|
+
args.state.run_start ||= Kernel.tick_count
|
|
10
|
+
|
|
11
|
+
# 6 frames, 3 ticks each, looping
|
|
12
|
+
tile_index = args.state.run_start.frame_index(6, 3, true)
|
|
13
|
+
|
|
14
|
+
args.outputs.sprites << {
|
|
15
|
+
x: args.state.player.x,
|
|
16
|
+
y: args.state.player.y,
|
|
17
|
+
w: args.state.player.w,
|
|
18
|
+
h: args.state.player.h,
|
|
19
|
+
path: 'sprites/horizontal-run.png',
|
|
20
|
+
tile_x: tile_index * 64,
|
|
21
|
+
tile_y: 0,
|
|
22
|
+
tile_w: 64,
|
|
23
|
+
tile_h: 64,
|
|
24
|
+
flip_horizontally: args.inputs.left
|
|
25
|
+
}
|
|
26
|
+
else
|
|
27
|
+
args.state.run_start = nil
|
|
28
|
+
|
|
29
|
+
args.outputs.sprites << {
|
|
30
|
+
x: args.state.player.x,
|
|
31
|
+
y: args.state.player.y,
|
|
32
|
+
w: args.state.player.w,
|
|
33
|
+
h: args.state.player.h,
|
|
34
|
+
path: 'sprites/horizontal-stand.png'
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
args.outputs.labels << { x: 640, y: 100, text: "Hold LEFT/RIGHT to run", anchor_x: 0.5 }
|
|
39
|
+
end
|