Compare commits
5 commits
a7ba4867ea
...
07442dd0f0
| Author | SHA1 | Date | |
|---|---|---|---|
| 07442dd0f0 | |||
| 3a85dd818d | |||
| 50d6f1e5d2 | |||
| a46667df6f | |||
| 43a6cc376b |
294 changed files with 26194 additions and 7 deletions
72
Makefile
72
Makefile
|
|
@ -96,6 +96,78 @@ install-hooks:
|
|||
echo "Linked $$hooks_dir/$$name -> $$hook"; \
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Documentation / release
|
||||
#
|
||||
# Every piece of the docs pipeline already existed; nothing chained them, so the
|
||||
# site drifted 26 engine commits behind without anything noticing. These targets
|
||||
# are the chain.
|
||||
#
|
||||
# SITE_DIR is the mcrogueface.github.io checkout. Override if yours lives elsewhere:
|
||||
# make release-docs SITE_DIR=/path/to/mcrogueface.github.io
|
||||
# ---------------------------------------------------------------------------
|
||||
SITE_DIR ?= $(CURDIR)/../mcrogueface.github.io
|
||||
|
||||
# Two DIFFERENT refs, easily conflated:
|
||||
#
|
||||
# BASE_REF the PREVIOUS release. The API delta is measured against it, to answer
|
||||
# "what changed, and which pages does that oblige us to revisit?"
|
||||
# RELEASE_REF the tag being CUT. The site pins its source links to it, which is what
|
||||
# freezes this version into the site's history.
|
||||
#
|
||||
# When cutting a release, set both:
|
||||
# make release-docs BASE_REF=0.2.8 RELEASE_REF=0.2.9
|
||||
#
|
||||
# Note: BASE_REF must carry api/manifest.json. The manifest infrastructure landed in
|
||||
# 54624b3, so tags older than that (0.2.8 included) have no baseline to diff against --
|
||||
# api_delta will say so rather than inventing one.
|
||||
BASE_REF ?= $(shell git describe --tags --abbrev=0 2>/dev/null)
|
||||
RELEASE_REF ?= $(shell git describe --tags --always --abbrev=0 2>/dev/null)
|
||||
|
||||
# Regenerate everything derived from the compiled module: man page, API reference
|
||||
# (HTML + Markdown), type stubs, and the tracked api/manifest.json.
|
||||
docs: linux
|
||||
@./tools/generate_all_docs.sh
|
||||
@./build/mcrogueface --headless --exec ../tools/generate_api_manifest.py
|
||||
|
||||
# Re-run every docs snippet and stamp its `# mcrf:` header with what actually
|
||||
# happened (status from the run, verified from the engine, objects from the source).
|
||||
stamp-snippets: linux
|
||||
@python3 tools/stamp_snippets.py
|
||||
|
||||
# CI gate: fail if any snippet is broken or its stamp is stale. Writes nothing.
|
||||
check-snippets: linux
|
||||
@python3 tools/stamp_snippets.py --check
|
||||
|
||||
# What changed in the API since the last release, and which site pages that
|
||||
# obligates you to update (resolved via each page's `mcrf.objects` frontmatter).
|
||||
api-delta:
|
||||
@python3 tools/api_delta.py $(BASE_REF) . --site-dir $(SITE_DIR) --format md
|
||||
|
||||
# The full release documentation pass. Run this when cutting a tag: it refreshes
|
||||
# everything the engine derives, proves the published samples still run, rebuilds
|
||||
# the site's generated reference + snippet library against this engine, and prints
|
||||
# the checklist of hand-written pages the API change obligates you to revisit.
|
||||
#
|
||||
# Deliberately NOT automatic: it does not commit, and it does not touch the
|
||||
# hand-written pages. It tells you what it found and leaves the judgment to you.
|
||||
release-docs: docs stamp-snippets
|
||||
@echo ""
|
||||
@echo "=== Regenerating the site against this engine (SITE_DIR=$(SITE_DIR)) ==="
|
||||
@test -d "$(SITE_DIR)" || { echo "SITE_DIR not found: $(SITE_DIR)"; exit 1; }
|
||||
@cd "$(SITE_DIR)" && python3 tools/build_reference.py
|
||||
@cd "$(SITE_DIR)" && python3 tools/build_library.py --ref $(RELEASE_REF)
|
||||
@echo ""
|
||||
@echo "=== API delta $(BASE_REF) -> now, and the pages it obligates ==="
|
||||
@python3 tools/api_delta.py $(BASE_REF) . --site-dir $(SITE_DIR) --format md
|
||||
@echo ""
|
||||
@echo "Site pinned to RELEASE_REF=$(RELEASE_REF); delta measured from BASE_REF=$(BASE_REF)."
|
||||
@echo "Next: review the delta above, update the hand-written pages it names,"
|
||||
@echo " then commit both repos. For a Gitea checklist issue instead, run:"
|
||||
@echo " python3 tools/api_delta.py $(BASE_REF) . --site-dir $(SITE_DIR) --format gitea"
|
||||
|
||||
.PHONY: docs stamp-snippets check-snippets api-delta release-docs
|
||||
|
||||
# Debug and sanitizer targets
|
||||
debug:
|
||||
@echo "Building McRogueFace with debug Python (pydebug assertions)..."
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -184,6 +184,8 @@ void CommandLineParser::print_help() {
|
|||
<< "McRogueFace specific options:\n"
|
||||
<< " --exec file : execute script before main program (can be used multiple times)\n"
|
||||
<< " --headless : run without creating a window (implies --audio-off)\n"
|
||||
<< " --run-forever : keep running after --exec scripts finish, instead of\n"
|
||||
<< " exiting (for a long-lived headless process)\n"
|
||||
<< " --audio-off : disable audio\n"
|
||||
<< " --audio-on : enable audio (even in headless mode)\n"
|
||||
<< " --screenshot [path] : take a screenshot in headless mode\n"
|
||||
|
|
|
|||
|
|
@ -257,12 +257,24 @@ void GameEngine::changeScene(std::string sceneName, TransitionType transitionTyp
|
|||
return;
|
||||
}
|
||||
|
||||
if (transitionType == TransitionType::None || duration <= 0.0f)
|
||||
// #379: a transition needs something to transition FROM. Until the first Python
|
||||
// scene is activated the engine sits on the internal "uitest" bootstrap scene, which
|
||||
// is not a PyScene and has no Python wrapper -- so mcrfpy.current_scene reports None
|
||||
// for it. Because current_scene reports the OUTGOING scene until a transition
|
||||
// completes, fading from the bootstrap left current_scene as None for the whole
|
||||
// transition, right after the user had assigned it. Nothing meaningful to transition
|
||||
// from means change now.
|
||||
const bool have_outgoing_scene =
|
||||
!scene.empty() &&
|
||||
scenes.find(scene) != scenes.end() &&
|
||||
dynamic_cast<PyScene*>(scenes[scene].get()) != nullptr;
|
||||
|
||||
if (transitionType == TransitionType::None || duration <= 0.0f || !have_outgoing_scene)
|
||||
{
|
||||
// Immediate scene change
|
||||
std::string old_scene = scene;
|
||||
scene = sceneName;
|
||||
|
||||
|
||||
// Trigger Python scene lifecycle events
|
||||
McRFPy_API::triggerSceneChange(old_scene, sceneName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -330,11 +330,22 @@ static PyMethodDef mcrfpyMethods[] = {
|
|||
{"step", McRFPy_API::_step, METH_VARARGS,
|
||||
MCRF_METHOD(mcrfpy, step,
|
||||
MCRF_SIG("(dt: float = None)", "float"),
|
||||
MCRF_DESC("Advance simulation time (headless mode only)."),
|
||||
MCRF_DESC("Run one full simulation frame (headless mode only). Advances the scene "
|
||||
"update, timers, Python Scene.update() callbacks, animations, and scene "
|
||||
"transitions (including their completion), records frame-time metrics, and "
|
||||
"increments the frame counter -- everything a windowed frame does except "
|
||||
"render and input.")
|
||||
MCRF_ARGS_START
|
||||
MCRF_ARG("dt", "Time to advance in seconds. If None, advances to the next scheduled event (timer/animation).")
|
||||
MCRF_RETURNS("float: Actual time advanced in seconds. Returns 0.0 in windowed mode.")
|
||||
MCRF_NOTE("In windowed mode, this is a no-op and returns 0.0. Use this for deterministic simulation control in headless/testing scenarios.")
|
||||
MCRF_NOTE("Rendering is deliberately excluded: it is orthogonal to the clock and costs "
|
||||
"zero simulation time, so a screenshot draws arbitrary state without time "
|
||||
"passing. In windowed mode this is a no-op returning 0.0. Timers run on "
|
||||
"simulation time -- explicit, deterministic time is the point of driving a "
|
||||
"headless test with step().")
|
||||
MCRF_NOTE("A headless --exec script must end by calling sys.exit(), because step() is "
|
||||
"the only headless clock: without it the engine cannot advance itself. Pass "
|
||||
"--run-forever for a long-lived headless process that drives itself.")
|
||||
)},
|
||||
{"exit", McRFPy_API::_exit, METH_NOARGS,
|
||||
MCRF_METHOD(mcrfpy, exit,
|
||||
|
|
@ -1076,6 +1087,20 @@ PyStatus McRFPy_API::init_python_with_config(const McRogueFaceConfig& config)
|
|||
}
|
||||
|
||||
PyStatus status;
|
||||
|
||||
// #378: pre-initialize in UTF-8 mode. Without this, the filesystem encoding falls
|
||||
// back to ASCII -- so open() with no explicit encoding= cannot read a UTF-8 file,
|
||||
// even though sys.getdefaultencoding() and the locale both say UTF-8. This is the
|
||||
// init path main.cpp uses; init_python() above has always done this correctly.
|
||||
PyPreConfig preconfig;
|
||||
PyPreConfig_InitIsolatedConfig(&preconfig);
|
||||
preconfig.utf8_mode = 1;
|
||||
|
||||
status = Py_PreInitialize(&preconfig);
|
||||
if (PyStatus_Exception(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
PyConfig pyconfig;
|
||||
PyConfig_InitIsolatedConfig(&pyconfig);
|
||||
|
||||
|
|
|
|||
85
tests/regression/issue_378_utf8_source_test.py
Normal file
85
tests/regression/issue_378_utf8_source_test.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Issue #378: the embedded interpreter's filesystem encoding must be UTF-8.
|
||||
|
||||
init_python_with_config() -- the path main.cpp actually uses -- did no
|
||||
pre-initialization at all, so PyPreConfig.utf8_mode was never enabled and the
|
||||
filesystem encoding fell back to ASCII. sys.getdefaultencoding() said utf-8 and the
|
||||
locale said UTF-8; only the filesystem encoding disagreed. (init_python(), the other
|
||||
init path, had always set utf8_mode = 1 correctly.)
|
||||
|
||||
open() with no explicit encoding= defaults to the filesystem encoding, so it could not
|
||||
read a UTF-8 file -- a degree sign was enough:
|
||||
|
||||
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 ...
|
||||
|
||||
In any normal CPython 3, open() defaults to UTF-8. This broke any script reading a data
|
||||
file, a save file, or its own source.
|
||||
|
||||
NOT affected, despite the folklore that "--exec scripts must be ASCII-only": --exec on
|
||||
a source file containing non-ASCII characters always worked. The C++ side reads the
|
||||
file and hands the bytes to Python, which parses them as UTF-8 per PEP 3120. The
|
||||
folklore was pointing at open(), via harnesses that read scripts themselves.
|
||||
|
||||
This file contains non-ASCII characters and reads itself back with a bare open(), so it
|
||||
exercises both halves.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
failures = []
|
||||
|
||||
|
||||
def check(cond, msg):
|
||||
if not cond:
|
||||
failures.append(msg)
|
||||
print(f"FAIL: {msg}")
|
||||
else:
|
||||
print(f" ok: {msg}")
|
||||
|
||||
|
||||
# Non-ASCII in a comment -- rotate the camera 90 degrees clockwise
|
||||
# The characters that broke the docs snippets: degree, bullet
|
||||
DEGREE = "\N{DEGREE SIGN}"
|
||||
BULLET = "\N{BULLET}"
|
||||
|
||||
|
||||
def test_filesystem_encoding():
|
||||
enc = sys.getfilesystemencoding()
|
||||
check(enc.lower().replace("-", "") == "utf8",
|
||||
f"sys.getfilesystemencoding() is utf-8 (got {enc!r})")
|
||||
check(sys.getdefaultencoding().lower().replace("-", "") == "utf8",
|
||||
"sys.getdefaultencoding() is utf-8")
|
||||
|
||||
|
||||
def test_source_literals_survived_the_parse():
|
||||
"""If the loader had used ASCII, this file would not have parsed at all."""
|
||||
check(DEGREE == "°", "a degree sign round-trips through the source")
|
||||
check(BULLET == "•", "a bullet round-trips through the source")
|
||||
|
||||
|
||||
def test_open_defaults_to_utf8():
|
||||
"""open() with no encoding= must read UTF-8, like any other Python 3."""
|
||||
path = __file__
|
||||
with open(path) as f:
|
||||
text = f.read()
|
||||
check("DEGREE SIGN" in text,
|
||||
"open() with no encoding= read this file back without a UnicodeDecodeError")
|
||||
|
||||
|
||||
def main():
|
||||
test_filesystem_encoding()
|
||||
test_source_literals_survived_the_parse()
|
||||
test_open_defaults_to_utf8()
|
||||
|
||||
if failures:
|
||||
print(f"\nFAILED ({len(failures)} checks)")
|
||||
for f in failures:
|
||||
print(f" - {f}")
|
||||
sys.exit(1)
|
||||
print("\nPASS")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
104
tests/regression/issue_379_first_scene_transition_test.py
Normal file
104
tests/regression/issue_379_first_scene_transition_test.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Issue #379: a transition into the FIRST scene must not leave current_scene as None.
|
||||
|
||||
While a transition runs, mcrfpy.current_scene reports the OUTGOING scene -- "current"
|
||||
means "what is on screen". That is a sane convention for scene->scene. But at startup
|
||||
there is no outgoing scene, so configuring default_transition before the first
|
||||
activation made the engine fade from nothing, and current_scene read None for the
|
||||
transition's entire duration:
|
||||
|
||||
mcrfpy.default_transition = mcrfpy.Transition.FADE
|
||||
mcrfpy.default_transition_duration = 1.0
|
||||
mcrfpy.current_scene = s
|
||||
print(mcrfpy.current_scene) # None -- assigned one line ago
|
||||
|
||||
A transition with nothing to transition from is not a transition. changeScene() now
|
||||
takes the immediate path when there is no outgoing scene.
|
||||
|
||||
Found by the tests/snippets/ gate: 062_scene_transition_fade.py and
|
||||
063_scene_transition_slide.py both configure a transition and then activate their first
|
||||
scene, which is the natural way to write that sample.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import mcrfpy
|
||||
|
||||
failures = []
|
||||
|
||||
|
||||
def check(cond, msg):
|
||||
if not cond:
|
||||
failures.append(msg)
|
||||
print(f"FAIL: {msg}")
|
||||
else:
|
||||
print(f" ok: {msg}")
|
||||
|
||||
|
||||
def frame():
|
||||
return mcrfpy.Frame(pos=(0, 0), size=(10, 10))
|
||||
|
||||
|
||||
def test_first_scene_with_transition_is_immediate():
|
||||
"""The bug: no outgoing scene means nothing to fade from."""
|
||||
s = mcrfpy.Scene("first")
|
||||
s.children.append(frame())
|
||||
|
||||
mcrfpy.default_transition = mcrfpy.Transition.FADE
|
||||
mcrfpy.default_transition_duration = 1.0
|
||||
mcrfpy.current_scene = s
|
||||
|
||||
got = mcrfpy.current_scene
|
||||
check(got is not None, "current_scene is not None right after the first activation")
|
||||
check(got is not None and got.name == "first",
|
||||
f"current_scene is the scene we activated (got {got!r})")
|
||||
|
||||
mcrfpy.step(0.016)
|
||||
got = mcrfpy.current_scene
|
||||
check(got is not None and got.name == "first",
|
||||
"it is still the activated scene after a step")
|
||||
|
||||
|
||||
def test_scene_to_scene_transition_still_defers():
|
||||
"""The existing convention must not regress: mid-transition, current_scene is the
|
||||
OUTGOING scene, and only becomes the incoming one on completion."""
|
||||
a = mcrfpy.Scene("from_a")
|
||||
a.children.append(frame())
|
||||
b = mcrfpy.Scene("to_b")
|
||||
b.children.append(frame())
|
||||
|
||||
mcrfpy.default_transition = mcrfpy.Transition.NONE
|
||||
mcrfpy.default_transition_duration = 0.0
|
||||
mcrfpy.current_scene = a
|
||||
check(mcrfpy.current_scene.name == "from_a", "scene a is active with no transition")
|
||||
|
||||
mcrfpy.default_transition = mcrfpy.Transition.FADE
|
||||
mcrfpy.default_transition_duration = 1.0
|
||||
mcrfpy.current_scene = b
|
||||
|
||||
mcrfpy.step(0.1)
|
||||
check(mcrfpy.current_scene.name == "from_a",
|
||||
"mid-transition, current_scene is still the outgoing scene")
|
||||
|
||||
for _ in range(20):
|
||||
mcrfpy.step(0.1)
|
||||
check(mcrfpy.current_scene.name == "to_b",
|
||||
"after the transition completes, current_scene is the incoming scene")
|
||||
|
||||
|
||||
def main():
|
||||
test_first_scene_with_transition_is_immediate()
|
||||
test_scene_to_scene_transition_still_defers()
|
||||
|
||||
if failures:
|
||||
print(f"\nFAILED ({len(failures)} checks)")
|
||||
for f in failures:
|
||||
print(f" - {f}")
|
||||
sys.exit(1)
|
||||
print("\nPASS")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -41,7 +41,17 @@ SANITIZER_PATTERNS = [
|
|||
]
|
||||
|
||||
# Test directories to run (in order)
|
||||
TEST_DIRS = ['unit', 'integration', 'regression', 'demo']
|
||||
TEST_DIRS = ['unit', 'integration', 'regression', 'demo', 'snippets']
|
||||
|
||||
# tests/snippets/ holds the code samples published on mcrogueface.github.io. They live
|
||||
# here, in the engine repo, so that breaking the API breaks the build -- the docs site
|
||||
# pulls them from here rather than keeping its own copy to rot in parallel. Before this,
|
||||
# 130 snippets were executed by nothing at all and carried a hand-typed "status=ok"
|
||||
# header that no run had ever verified.
|
||||
#
|
||||
# They are display scripts with no sys.exit(), because they must stay copy-pasteable, so
|
||||
# _harness.py is chained as a second --exec to supply the ending (see its docstring).
|
||||
SNIPPET_HARNESS = 'tests/snippets/_harness.py'
|
||||
|
||||
# #372: tests/demo/ was never run by anything, so the demo screens -- which CLAUDE.md
|
||||
# points at as the canonical API-usage examples -- silently bitrotted until they could
|
||||
|
|
@ -111,6 +121,10 @@ def run_test(test_path, verbose=False, timeout=DEFAULT_TIMEOUT,
|
|||
|
||||
cmd.extend([str(MCROGUEFACE), '--headless', '--exec', str(test_path)])
|
||||
|
||||
# A snippet has no ending of its own; chain the harness to supply one.
|
||||
if test_path.parent.name == 'snippets':
|
||||
cmd.extend(['--exec', str(TESTS_DIR / 'snippets' / '_harness.py')])
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
|
|
@ -171,7 +185,8 @@ def find_tests(directory):
|
|||
if directory == 'demo':
|
||||
# #372: allowlisted entry points only -- see DEMO_SMOKE_TESTS.
|
||||
return [test_dir / name for name in DEMO_SMOKE_TESTS if (test_dir / name).exists()]
|
||||
return sorted(test_dir.glob("*.py"))
|
||||
# Leading underscore = support file, not a test (tests/snippets/_harness.py).
|
||||
return sorted(p for p in test_dir.glob("*.py") if not p.name.startswith('_'))
|
||||
|
||||
def main():
|
||||
verbose = '-v' in sys.argv or '--verbose' in sys.argv
|
||||
|
|
|
|||
17
tests/snippets/001_hello_frame.py
Normal file
17
tests/snippets/001_hello_frame.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# mcrf: objects=[Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Hello Frame - The simplest UI element
|
||||
import mcrfpy
|
||||
|
||||
# Create a scene and make it active
|
||||
scene = mcrfpy.Scene("hello")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Create a big, colorful frame in the center
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(312, 234),
|
||||
size=(400, 300),
|
||||
fill_color=mcrfpy.Color(100, 150, 200),
|
||||
outline_color=mcrfpy.Color(255, 255, 255),
|
||||
outline=4.0
|
||||
)
|
||||
scene.children.append(frame)
|
||||
16
tests/snippets/002_hello_caption.py
Normal file
16
tests/snippets/002_hello_caption.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# mcrf: objects=[Caption,Color,Scene] verified=0.2.8-dev status=ok
|
||||
# Hello Caption - Display text on screen
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("hello")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Large centered text
|
||||
caption = mcrfpy.Caption(
|
||||
text="Hello, McRogueFace!",
|
||||
pos=(512, 350)
|
||||
)
|
||||
caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
# Center the text by adjusting position after creation
|
||||
caption.x -= caption.w / 2
|
||||
scene.children.append(caption)
|
||||
19
tests/snippets/003_hello_sprite.py
Normal file
19
tests/snippets/003_hello_sprite.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# mcrf: objects=[Caption,Scene,Sprite] verified=0.2.8-dev status=ok
|
||||
# Hello Sprite - Display a textured sprite
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("hello")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Create a large sprite using default texture
|
||||
sprite = mcrfpy.Sprite(
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84, # Knight character
|
||||
pos=(412, 284),
|
||||
scale=12.0
|
||||
)
|
||||
scene.children.append(sprite)
|
||||
|
||||
# Add a label
|
||||
label = mcrfpy.Caption(text="Sprite Index: 84", pos=(412, 550))
|
||||
scene.children.append(label)
|
||||
25
tests/snippets/004_hello_grid.py
Normal file
25
tests/snippets/004_hello_grid.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# mcrf: objects=[Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Hello Grid - Create a tile-based grid
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("hello")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Create a grid that fills the screen
|
||||
# zoom=4.0 with 16x16 texture means each tile is 64x64 pixels
|
||||
# 16 tiles * 64 pixels = 1024, 12 tiles * 64 pixels = 768
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Fill with floor tiles
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
cell.tilesprite = 48 # Floor tile
|
||||
cell.walkable = True
|
||||
29
tests/snippets/005_hello_entity.py
Normal file
29
tests/snippets/005_hello_entity.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# mcrf: objects=[Entity,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Hello Entity - Add entities to a grid
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("hello")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Create grid filling the screen
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Floor tiles
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
# Add a player entity
|
||||
player = mcrfpy.Entity(
|
||||
grid_pos=(8, 6),
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84 # Knight
|
||||
)
|
||||
grid.entities.append(player)
|
||||
33
tests/snippets/006_frame_colors.py
Normal file
33
tests/snippets/006_frame_colors.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Colors - Fill and outline colors
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Dark background
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
colors = [
|
||||
(mcrfpy.Color(255, 100, 100), "Red"),
|
||||
(mcrfpy.Color(100, 255, 100), "Green"),
|
||||
(mcrfpy.Color(100, 100, 255), "Blue"),
|
||||
(mcrfpy.Color(255, 255, 100), "Yellow"),
|
||||
]
|
||||
|
||||
for i, (color, name) in enumerate(colors):
|
||||
x = 112 + (i % 2) * 420
|
||||
y = 134 + (i // 2) * 280
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(x, y), size=(380, 240),
|
||||
fill_color=color,
|
||||
outline_color=mcrfpy.Color(255, 255, 255),
|
||||
outline=3.0
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(text=name, pos=(x + 150, y + 100))
|
||||
label.outline = 2
|
||||
label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label)
|
||||
34
tests/snippets/007_frame_opacity.py
Normal file
34
tests/snippets/007_frame_opacity.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Opacity - Transparency levels
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Background pattern
|
||||
for i in range(20):
|
||||
stripe = mcrfpy.Frame(
|
||||
pos=(i * 52, 0), size=(26, 768),
|
||||
fill_color=mcrfpy.Color(50, 50, 80)
|
||||
)
|
||||
scene.children.append(stripe)
|
||||
|
||||
# Frames with varying opacity
|
||||
opacities = [1.0, 0.75, 0.5, 0.25]
|
||||
for i, opacity in enumerate(opacities):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(112 + i * 220, 234), size=(180, 300),
|
||||
fill_color=mcrfpy.Color(255, 150, 50),
|
||||
outline=2.0,
|
||||
outline_color=mcrfpy.Color(255, 255, 255),
|
||||
opacity=opacity
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(
|
||||
text=f"opacity={opacity}",
|
||||
pos=(112 + i * 220 + 30, 550)
|
||||
)
|
||||
label.outline = 2
|
||||
label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label)
|
||||
22
tests/snippets/008_frame_outline.py
Normal file
22
tests/snippets/008_frame_outline.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Outline - Border thickness
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
outlines = [0, 2, 5, 10, 20]
|
||||
for i, thickness in enumerate(outlines):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(92 + i * 180, 234), size=(160, 300),
|
||||
fill_color=mcrfpy.Color(60, 80, 120),
|
||||
outline_color=mcrfpy.Color(255, 200, 100),
|
||||
outline=thickness
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(
|
||||
text=f"outline={thickness}",
|
||||
pos=(92 + i * 180 + 30, 550)
|
||||
)
|
||||
scene.children.append(label)
|
||||
41
tests/snippets/009_frame_children.py
Normal file
41
tests/snippets/009_frame_children.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Children - Nested UI elements
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Parent container
|
||||
parent = mcrfpy.Frame(
|
||||
pos=(162, 134), size=(700, 500),
|
||||
fill_color=mcrfpy.Color(40, 40, 60),
|
||||
outline=3.0,
|
||||
outline_color=mcrfpy.Color(100, 100, 150)
|
||||
)
|
||||
scene.children.append(parent)
|
||||
|
||||
# Child frames - positions relative to parent
|
||||
child1 = mcrfpy.Frame(
|
||||
pos=(20, 20), size=(200, 200),
|
||||
fill_color=mcrfpy.Color(200, 80, 80)
|
||||
)
|
||||
parent.children.append(child1)
|
||||
|
||||
child2 = mcrfpy.Frame(
|
||||
pos=(240, 20), size=(200, 200),
|
||||
fill_color=mcrfpy.Color(80, 200, 80)
|
||||
)
|
||||
parent.children.append(child2)
|
||||
|
||||
child3 = mcrfpy.Frame(
|
||||
pos=(460, 20), size=(200, 200),
|
||||
fill_color=mcrfpy.Color(80, 80, 200)
|
||||
)
|
||||
parent.children.append(child3)
|
||||
|
||||
# Label
|
||||
label = mcrfpy.Caption(
|
||||
text="Children use parent-relative coordinates",
|
||||
pos=(150, 450)
|
||||
)
|
||||
parent.children.append(label)
|
||||
42
tests/snippets/010_frame_clip_children.py
Normal file
42
tests/snippets/010_frame_clip_children.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Clipping - clip_children property
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Frame WITHOUT clipping
|
||||
no_clip = mcrfpy.Frame(
|
||||
pos=(100, 200), size=(300, 300),
|
||||
fill_color=mcrfpy.Color(60, 60, 100),
|
||||
outline=2.0, outline_color=mcrfpy.Color(150, 150, 200),
|
||||
clip_children=False
|
||||
)
|
||||
scene.children.append(no_clip)
|
||||
|
||||
overflow1 = mcrfpy.Frame(
|
||||
pos=(150, 150), size=(200, 200),
|
||||
fill_color=mcrfpy.Color(255, 100, 100)
|
||||
)
|
||||
no_clip.children.append(overflow1)
|
||||
|
||||
label1 = mcrfpy.Caption(text="clip_children=False", pos=(120, 520))
|
||||
scene.children.append(label1)
|
||||
|
||||
# Frame WITH clipping
|
||||
with_clip = mcrfpy.Frame(
|
||||
pos=(524, 200), size=(300, 300),
|
||||
fill_color=mcrfpy.Color(60, 60, 100),
|
||||
outline=2.0, outline_color=mcrfpy.Color(150, 150, 200),
|
||||
clip_children=True
|
||||
)
|
||||
scene.children.append(with_clip)
|
||||
|
||||
overflow2 = mcrfpy.Frame(
|
||||
pos=(150, 150), size=(200, 200),
|
||||
fill_color=mcrfpy.Color(100, 255, 100)
|
||||
)
|
||||
with_clip.children.append(overflow2)
|
||||
|
||||
label2 = mcrfpy.Caption(text="clip_children=True", pos=(544, 520))
|
||||
scene.children.append(label2)
|
||||
48
tests/snippets/011_frame_z_index.py
Normal file
48
tests/snippets/011_frame_z_index.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Z-Index - Control rendering order
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Dark background
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Create overlapping frames with different z_index
|
||||
# Lower z_index = rendered first (behind)
|
||||
back = mcrfpy.Frame(
|
||||
pos=(262, 184), size=(300, 300),
|
||||
fill_color=mcrfpy.Color(255, 100, 100),
|
||||
z_index=0
|
||||
)
|
||||
scene.children.append(back)
|
||||
|
||||
middle = mcrfpy.Frame(
|
||||
pos=(362, 234), size=(300, 300),
|
||||
fill_color=mcrfpy.Color(100, 255, 100),
|
||||
z_index=1
|
||||
)
|
||||
scene.children.append(middle)
|
||||
|
||||
front = mcrfpy.Frame(
|
||||
pos=(462, 284), size=(300, 300),
|
||||
fill_color=mcrfpy.Color(100, 100, 255),
|
||||
z_index=2
|
||||
)
|
||||
scene.children.append(front)
|
||||
|
||||
# Labels with outlines for visibility
|
||||
label1 = mcrfpy.Caption(text="z_index=0 (back)", pos=(262, 500))
|
||||
label1.outline = 2
|
||||
label1.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label1)
|
||||
|
||||
label2 = mcrfpy.Caption(text="z_index=1 (middle)", pos=(362, 550))
|
||||
label2.outline = 2
|
||||
label2.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label2)
|
||||
|
||||
label3 = mcrfpy.Caption(text="z_index=2 (front)", pos=(462, 600))
|
||||
label3.outline = 2
|
||||
label3.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label3)
|
||||
32
tests/snippets/012_frame_visible.py
Normal file
32
tests/snippets/012_frame_visible.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Visibility - Show and hide elements
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Create three frames
|
||||
visible_frame = mcrfpy.Frame(
|
||||
pos=(100, 234), size=(250, 300),
|
||||
fill_color=mcrfpy.Color(100, 200, 100),
|
||||
visible=True
|
||||
)
|
||||
scene.children.append(visible_frame)
|
||||
scene.children.append(mcrfpy.Caption(text="visible=True", pos=(140, 550)))
|
||||
|
||||
hidden_frame = mcrfpy.Frame(
|
||||
pos=(387, 234), size=(250, 300),
|
||||
fill_color=mcrfpy.Color(200, 100, 100),
|
||||
visible=False # This won't be displayed
|
||||
)
|
||||
scene.children.append(hidden_frame)
|
||||
scene.children.append(mcrfpy.Caption(text="visible=False", pos=(427, 550)))
|
||||
scene.children.append(mcrfpy.Caption(text="(frame exists but hidden)", pos=(397, 580)))
|
||||
|
||||
visible_frame2 = mcrfpy.Frame(
|
||||
pos=(674, 234), size=(250, 300),
|
||||
fill_color=mcrfpy.Color(100, 100, 200),
|
||||
visible=True
|
||||
)
|
||||
scene.children.append(visible_frame2)
|
||||
scene.children.append(mcrfpy.Caption(text="visible=True", pos=(714, 550)))
|
||||
41
tests/snippets/013_frame_name.py
Normal file
41
tests/snippets/013_frame_name.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Name - Named elements for lookup
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Create named frames
|
||||
header = mcrfpy.Frame(
|
||||
pos=(112, 50), size=(800, 100),
|
||||
fill_color=mcrfpy.Color(80, 80, 120),
|
||||
name="header"
|
||||
)
|
||||
scene.children.append(header)
|
||||
|
||||
sidebar = mcrfpy.Frame(
|
||||
pos=(112, 170), size=(200, 500),
|
||||
fill_color=mcrfpy.Color(60, 100, 80),
|
||||
name="sidebar"
|
||||
)
|
||||
scene.children.append(sidebar)
|
||||
|
||||
content = mcrfpy.Frame(
|
||||
pos=(332, 170), size=(580, 500),
|
||||
fill_color=mcrfpy.Color(100, 80, 60),
|
||||
name="main_content"
|
||||
)
|
||||
scene.children.append(content)
|
||||
|
||||
# Find elements by name
|
||||
found = mcrfpy.find("header")
|
||||
if found:
|
||||
label = mcrfpy.Caption(
|
||||
text=f"Found: '{found.name}'",
|
||||
pos=(20, 30)
|
||||
)
|
||||
found.children.append(label)
|
||||
|
||||
# Display all names
|
||||
scene.children.append(mcrfpy.Caption(text="sidebar", pos=(170, 400)))
|
||||
scene.children.append(mcrfpy.Caption(text="main_content", pos=(550, 400)))
|
||||
29
tests/snippets/014_frame_rotation.py
Normal file
29
tests/snippets/014_frame_rotation.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Rotation - Rotate UI elements
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Dark background
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
rotations = [0, 15, 30, 45, 60]
|
||||
for i, angle in enumerate(rotations):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100 + i * 180, 300), size=(120, 120),
|
||||
fill_color=mcrfpy.Color(150, 100, 200),
|
||||
outline=3.0,
|
||||
outline_color=mcrfpy.Color(255, 255, 255)
|
||||
)
|
||||
frame.rotation = angle
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(text=f"{angle}deg", pos=(130 + i * 180, 500))
|
||||
label.outline = 2
|
||||
label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label)
|
||||
|
||||
title = mcrfpy.Caption(text="Frame Rotation", pos=(420, 100))
|
||||
title.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(title)
|
||||
45
tests/snippets/015_frame_origin.py
Normal file
45
tests/snippets/015_frame_origin.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Vector] verified=0.2.8-dev status=ok
|
||||
# Frame Origin - Rotation pivot point
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Dark background
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# All frames rotated 45 degrees, different origins
|
||||
origins = [
|
||||
((0, 0), "top-left"),
|
||||
((60, 60), "center"),
|
||||
((120, 0), "top-right"),
|
||||
((0, 120), "bottom-left"),
|
||||
]
|
||||
|
||||
for i, (origin, name) in enumerate(origins):
|
||||
x = 150 + (i % 2) * 400
|
||||
y = 180 + (i // 2) * 280
|
||||
|
||||
# Reference frame (no rotation)
|
||||
ref = mcrfpy.Frame(
|
||||
pos=(x, y), size=(120, 120),
|
||||
fill_color=mcrfpy.Color(50, 50, 50),
|
||||
outline=1.0,
|
||||
outline_color=mcrfpy.Color(100, 100, 100)
|
||||
)
|
||||
scene.children.append(ref)
|
||||
|
||||
# Rotated frame
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(x, y), size=(120, 120),
|
||||
fill_color=mcrfpy.Color(200, 100, 150),
|
||||
opacity=0.8
|
||||
)
|
||||
frame.rotation = 45
|
||||
frame.origin = mcrfpy.Vector(origin[0], origin[1])
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(text=f"origin: {name}", pos=(x, y + 160))
|
||||
label.outline = 2
|
||||
label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label)
|
||||
43
tests/snippets/016_frame_alignment.py
Normal file
43
tests/snippets/016_frame_alignment.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# mcrf: objects=[Alignment,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Frame Alignment - Automatic positioning
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Container to show alignment
|
||||
container = mcrfpy.Frame(
|
||||
pos=(112, 84), size=(800, 600),
|
||||
fill_color=mcrfpy.Color(40, 40, 60),
|
||||
outline=2.0
|
||||
)
|
||||
scene.children.append(container)
|
||||
|
||||
# Grid of alignment options (excluding CENTER to avoid margin error)
|
||||
alignments = [
|
||||
(mcrfpy.Alignment.TOP_LEFT, "TOP_LEFT"),
|
||||
(mcrfpy.Alignment.TOP_CENTER, "TOP_CENTER"),
|
||||
(mcrfpy.Alignment.TOP_RIGHT, "TOP_RIGHT"),
|
||||
(mcrfpy.Alignment.CENTER_LEFT, "CENTER_LEFT"),
|
||||
(mcrfpy.Alignment.CENTER_RIGHT, "CENTER_RIGHT"),
|
||||
(mcrfpy.Alignment.BOTTOM_LEFT, "BOTTOM_LEFT"),
|
||||
(mcrfpy.Alignment.BOTTOM_CENTER, "BOTTOM_CENTER"),
|
||||
(mcrfpy.Alignment.BOTTOM_RIGHT, "BOTTOM_RIGHT"),
|
||||
]
|
||||
|
||||
for alignment, name in alignments:
|
||||
box = mcrfpy.Frame(
|
||||
size=(80, 60),
|
||||
fill_color=mcrfpy.Color(150, 100, 200),
|
||||
align=alignment,
|
||||
margin=10
|
||||
)
|
||||
container.children.append(box)
|
||||
|
||||
# Center box (no margin for CENTER)
|
||||
center_box = mcrfpy.Frame(
|
||||
size=(80, 60),
|
||||
fill_color=mcrfpy.Color(100, 200, 150),
|
||||
align=mcrfpy.Alignment.CENTER
|
||||
)
|
||||
container.children.append(center_box)
|
||||
26
tests/snippets/017_caption_styling.py
Normal file
26
tests/snippets/017_caption_styling.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# mcrf: objects=[Caption,Color,Scene] verified=0.2.8-dev status=ok
|
||||
# Caption Styling - Text colors and fonts
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Title
|
||||
title = mcrfpy.Caption(text="Caption Styles", pos=(350, 80))
|
||||
title.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
scene.children.append(title)
|
||||
|
||||
# Different colors
|
||||
colors = [
|
||||
(mcrfpy.Color(255, 100, 100), "Red Text"),
|
||||
(mcrfpy.Color(100, 255, 100), "Green Text"),
|
||||
(mcrfpy.Color(100, 100, 255), "Blue Text"),
|
||||
(mcrfpy.Color(255, 255, 100), "Yellow Text"),
|
||||
(mcrfpy.Color(255, 100, 255), "Magenta Text"),
|
||||
(mcrfpy.Color(100, 255, 255), "Cyan Text"),
|
||||
]
|
||||
|
||||
for i, (color, text) in enumerate(colors):
|
||||
cap = mcrfpy.Caption(text=text, pos=(350, 180 + i * 80))
|
||||
cap.fill_color = color
|
||||
scene.children.append(cap)
|
||||
34
tests/snippets/018_caption_in_frame.py
Normal file
34
tests/snippets/018_caption_in_frame.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Caption in Frame - Text inside containers
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Card-style container with text
|
||||
card = mcrfpy.Frame(
|
||||
pos=(262, 184), size=(500, 400),
|
||||
fill_color=mcrfpy.Color(50, 60, 80),
|
||||
outline=3.0,
|
||||
outline_color=mcrfpy.Color(100, 120, 160)
|
||||
)
|
||||
scene.children.append(card)
|
||||
|
||||
# Title in card
|
||||
card_title = mcrfpy.Caption(text="Player Stats", pos=(180, 30))
|
||||
card_title.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
card.children.append(card_title)
|
||||
|
||||
# Stats
|
||||
stats = [
|
||||
"Health: 100/100",
|
||||
"Mana: 50/50",
|
||||
"Strength: 15",
|
||||
"Defense: 12",
|
||||
"Speed: 18",
|
||||
]
|
||||
|
||||
for i, stat in enumerate(stats):
|
||||
line = mcrfpy.Caption(text=stat, pos=(50, 100 + i * 50))
|
||||
line.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
card.children.append(line)
|
||||
36
tests/snippets/019_caption_dimensions.py
Normal file
36
tests/snippets/019_caption_dimensions.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Caption Dimensions - Text width and height
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
texts = [
|
||||
"Short",
|
||||
"A medium length text",
|
||||
"This is a much longer piece of text to display",
|
||||
]
|
||||
|
||||
y = 150
|
||||
for text in texts:
|
||||
# Create caption
|
||||
cap = mcrfpy.Caption(text=text, pos=(100, y))
|
||||
cap.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
scene.children.append(cap)
|
||||
|
||||
# Bounding box showing dimensions
|
||||
box = mcrfpy.Frame(
|
||||
pos=(100, y - 5),
|
||||
size=(cap.w, cap.h + 10),
|
||||
fill_color=mcrfpy.Color(0, 0, 0, 0),
|
||||
outline=1.0,
|
||||
outline_color=mcrfpy.Color(255, 100, 100)
|
||||
)
|
||||
scene.children.append(box)
|
||||
|
||||
# Show dimensions
|
||||
dims = mcrfpy.Caption(text=f"w={cap.w:.0f}, h={cap.h:.0f}", pos=(100, y + 50))
|
||||
dims.fill_color = mcrfpy.Color(150, 150, 150)
|
||||
scene.children.append(dims)
|
||||
|
||||
y += 150
|
||||
28
tests/snippets/020_sprite_atlas.py
Normal file
28
tests/snippets/020_sprite_atlas.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# mcrf: objects=[Caption,Color,Scene,Sprite] verified=0.2.8-dev status=ok
|
||||
# Sprite Atlas - Browse texture atlas
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Display a grid of sprites from the default texture
|
||||
title = mcrfpy.Caption(text="Default Texture Atlas (kenney_tinydungeon)", pos=(250, 30))
|
||||
title.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(title)
|
||||
|
||||
# Show sprites 0-99 in a 10x10 grid
|
||||
for row in range(10):
|
||||
for col in range(10):
|
||||
idx = row * 10 + col
|
||||
sprite = mcrfpy.Sprite(
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=idx,
|
||||
pos=(112 + col * 80, 80 + row * 65),
|
||||
scale=3.0
|
||||
)
|
||||
scene.children.append(sprite)
|
||||
|
||||
# Index label
|
||||
label = mcrfpy.Caption(text=str(idx), pos=(112 + col * 80 + 20, 80 + row * 65 + 48))
|
||||
label.fill_color = mcrfpy.Color(120, 120, 120)
|
||||
scene.children.append(label)
|
||||
24
tests/snippets/021_sprite_scale.py
Normal file
24
tests/snippets/021_sprite_scale.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# mcrf: objects=[Caption,Color,Scene,Sprite] verified=0.2.8-dev status=ok
|
||||
# Sprite Scale - Resize sprites
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scales = [1.0, 2.0, 4.0, 8.0, 16.0]
|
||||
for i, scale in enumerate(scales):
|
||||
sprite = mcrfpy.Sprite(
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84, # Knight
|
||||
pos=(100 + i * 180, 300),
|
||||
scale=scale
|
||||
)
|
||||
scene.children.append(sprite)
|
||||
|
||||
label = mcrfpy.Caption(text=f"scale={scale}", pos=(100 + i * 180, 550))
|
||||
label.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
scene.children.append(label)
|
||||
|
||||
title = mcrfpy.Caption(text="Sprite Scaling", pos=(420, 100))
|
||||
title.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(title)
|
||||
32
tests/snippets/022_sprite_tint.py
Normal file
32
tests/snippets/022_sprite_tint.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# mcrf: objects=[Caption,Color,Scene,Sprite] verified=0.2.8-dev status=ok
|
||||
# Sprite Tint - Note: Sprite tinting requires shader
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Note: Direct sprite color tinting is not available
|
||||
# Use opacity or shaders for color effects
|
||||
|
||||
opacities = [1.0, 0.8, 0.6, 0.4, 0.2]
|
||||
labels = ["100%", "80%", "60%", "40%", "20%"]
|
||||
|
||||
for i, (opacity, label_text) in enumerate(zip(opacities, labels)):
|
||||
x = 100 + (i % 3) * 300
|
||||
y = 200 + (i // 3) * 280
|
||||
|
||||
sprite = mcrfpy.Sprite(
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84,
|
||||
pos=(x, y),
|
||||
scale=8.0
|
||||
)
|
||||
sprite.opacity = opacity
|
||||
scene.children.append(sprite)
|
||||
|
||||
label = mcrfpy.Caption(text=f"opacity={label_text}", pos=(x, y + 150))
|
||||
scene.children.append(label)
|
||||
|
||||
title = mcrfpy.Caption(text="Sprite Opacity Levels", pos=(380, 80))
|
||||
title.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(title)
|
||||
39
tests/snippets/023_grid_tiles.py
Normal file
39
tests/snippets/023_grid_tiles.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# mcrf: objects=[Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Grid Tiles - Different tile types
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Different terrain types
|
||||
tile_types = [
|
||||
(48, "Floor"), # Floor
|
||||
(1, "Wall"), # Wall
|
||||
(65, "Door"), # Door
|
||||
(17, "Chest"), # Chest
|
||||
(80, "Water"), # Water
|
||||
]
|
||||
|
||||
# Fill grid with pattern showing different tiles
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
# Border walls
|
||||
if x == 0 or x == 15 or y == 0 or y == 11:
|
||||
cell.tilesprite = 1
|
||||
else:
|
||||
# Show different tiles in sections
|
||||
section = (x - 1) // 3
|
||||
if section < len(tile_types):
|
||||
cell.tilesprite = tile_types[section][0]
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
37
tests/snippets/024_grid_walkable.py
Normal file
37
tests/snippets/024_grid_walkable.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# mcrf: objects=[Caption,Color,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Grid Walkable - Walkable vs blocked tiles
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Create maze-like pattern
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
# Outer walls
|
||||
if x == 0 or x == 15 or y == 0 or y == 11:
|
||||
cell.tilesprite = 1 # Wall
|
||||
cell.walkable = False
|
||||
# Inner obstacles
|
||||
elif (x + y) % 4 == 0 and x > 2 and x < 13:
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
else:
|
||||
cell.tilesprite = 48 # Floor
|
||||
cell.walkable = True
|
||||
|
||||
# Legend
|
||||
legend = mcrfpy.Caption(text="Walls are not walkable, floors are", pos=(350, 720))
|
||||
legend.outline = 2
|
||||
legend.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(legend)
|
||||
39
tests/snippets/025_grid_cell_color.py
Normal file
39
tests/snippets/025_grid_cell_color.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Grid Cell Colors - Use color layer for cell colors
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Set tiles
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
# Add a color layer for tinting
|
||||
color_layer = mcrfpy.ColorLayer(z_index=1, name="tint")
|
||||
grid.add_layer(color_layer)
|
||||
|
||||
# Create colorful gradient pattern using color layer
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
# Rainbow gradient based on position
|
||||
r = int(255 * x / 16)
|
||||
g = int(255 * y / 12)
|
||||
b = int(255 * (16 - x) / 16)
|
||||
color_layer.set((x, y), mcrfpy.Color(r, g, b, 180))
|
||||
|
||||
title = mcrfpy.Caption(text="Per-Cell Color Tinting via Layer", pos=(350, 720))
|
||||
title.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
title.outline = 2
|
||||
title.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(title)
|
||||
60
tests/snippets/026_grid_camera_center.py
Normal file
60
tests/snippets/026_grid_camera_center.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,InputState,Key,Scene] verified=0.2.8-dev status=ok
|
||||
# Grid Camera Center - Pan the view
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Large grid, smaller viewport
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(30, 30),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(212, 134),
|
||||
size=(600, 500)
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Fill with pattern to show scrolling
|
||||
for y in range(30):
|
||||
for x in range(30):
|
||||
cell = grid.at(x, y)
|
||||
if (x + y) % 2 == 0:
|
||||
cell.tilesprite = 48
|
||||
else:
|
||||
cell.tilesprite = 49
|
||||
|
||||
# Center camera on specific tile (15, 15)
|
||||
grid.center_camera((15, 15))
|
||||
|
||||
# Add marker at center
|
||||
marker = mcrfpy.Entity(
|
||||
grid_pos=(15, 15),
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84
|
||||
)
|
||||
grid.entities.append(marker)
|
||||
|
||||
status = mcrfpy.Caption(text="WASD to pan camera - centered on (15, 15)", pos=(300, 660))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
|
||||
# Track camera position
|
||||
camera_pos = [15, 15]
|
||||
|
||||
def on_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED:
|
||||
return
|
||||
dx, dy = 0, 0
|
||||
if key == mcrfpy.Key.W: dy = -1
|
||||
elif key == mcrfpy.Key.S: dy = 1
|
||||
elif key == mcrfpy.Key.A: dx = -1
|
||||
elif key == mcrfpy.Key.D: dx = 1
|
||||
else: return
|
||||
|
||||
camera_pos[0] = max(0, min(29, camera_pos[0] + dx))
|
||||
camera_pos[1] = max(0, min(29, camera_pos[1] + dy))
|
||||
grid.center_camera((camera_pos[0], camera_pos[1]))
|
||||
status.text = f"WASD to pan camera - centered on ({camera_pos[0]}, {camera_pos[1]})"
|
||||
|
||||
scene.on_key = on_key
|
||||
41
tests/snippets/027_grid_zoom.py
Normal file
41
tests/snippets/027_grid_zoom.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Frame,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Grid Zoom - Scale the grid view
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
zooms = [0.5, 1.0, 1.5, 2.0]
|
||||
for i, zoom in enumerate(zooms):
|
||||
x = 62 + (i % 2) * 480
|
||||
y = 84 + (i // 2) * 340
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(10, 8),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(x, y),
|
||||
size=(420, 280)
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Fill with pattern
|
||||
for gy in range(8):
|
||||
for gx in range(10):
|
||||
cell = grid.at(gx, gy)
|
||||
cell.tilesprite = 48 if (gx + gy) % 2 == 0 else 49
|
||||
|
||||
# Add entity
|
||||
ent = mcrfpy.Entity(grid_pos=(5, 4), texture=mcrfpy.default_texture, sprite_index=84)
|
||||
grid.entities.append(ent)
|
||||
|
||||
# Apply zoom
|
||||
grid.zoom = zoom
|
||||
grid.center_camera((5, 4))
|
||||
|
||||
label = mcrfpy.Caption(text=f"zoom={zoom}", pos=(x + 170, y + 290))
|
||||
label.outline = 2
|
||||
label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label)
|
||||
|
||||
# Add background and title
|
||||
scene.children.insert(0, mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
49
tests/snippets/028_entity_movement.py
Normal file
49
tests/snippets/028_entity_movement.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,InputState,Key,Scene] verified=0.2.8-dev status=ok
|
||||
# Entity Movement - Move entities on grid
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Floor tiles
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
# Create player entity
|
||||
player = mcrfpy.Entity(
|
||||
grid_pos=(8, 6),
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84
|
||||
)
|
||||
grid.entities.append(player)
|
||||
|
||||
# Movement with keyboard
|
||||
def on_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED:
|
||||
return
|
||||
x, y = player.grid_x, player.grid_y
|
||||
if key == mcrfpy.Key.UP or key == mcrfpy.Key.W:
|
||||
player.grid_pos = (x, y - 1)
|
||||
elif key == mcrfpy.Key.DOWN or key == mcrfpy.Key.S:
|
||||
player.grid_pos = (x, y + 1)
|
||||
elif key == mcrfpy.Key.LEFT or key == mcrfpy.Key.A:
|
||||
player.grid_pos = (x - 1, y)
|
||||
elif key == mcrfpy.Key.RIGHT or key == mcrfpy.Key.D:
|
||||
player.grid_pos = (x + 1, y)
|
||||
|
||||
scene.on_key = on_key
|
||||
|
||||
status = mcrfpy.Caption(text="WASD or Arrow keys to move", pos=(380, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
44
tests/snippets/029_entity_multiple.py
Normal file
44
tests/snippets/029_entity_multiple.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Multiple Entities - Several entities on one grid
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Floor tiles
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
# Different entity types
|
||||
entities = [
|
||||
(4, 3, 84, "Knight"), # Knight
|
||||
(8, 3, 86, "Mage"), # Mage
|
||||
(12, 3, 88, "Archer"), # Archer
|
||||
(4, 8, 112, "Goblin"), # Goblin
|
||||
(8, 8, 116, "Skeleton"),# Skeleton
|
||||
(12, 8, 120, "Slime"), # Slime
|
||||
]
|
||||
|
||||
for x, y, sprite_idx, name in entities:
|
||||
entity = mcrfpy.Entity(
|
||||
grid_pos=(x, y),
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=sprite_idx,
|
||||
name=name
|
||||
)
|
||||
grid.entities.append(entity)
|
||||
|
||||
status = mcrfpy.Caption(text=f"Entity count: {len(grid.entities)}", pos=(420, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
48
tests/snippets/030_entity_draw_pos.py
Normal file
48
tests/snippets/030_entity_draw_pos.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,Scene,Timer,Vector] verified=0.2.8-dev status=ok
|
||||
# Entity Draw Position - Smooth sub-tile positioning
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Floor tiles
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
# Entity at grid position with draw offset
|
||||
entity = mcrfpy.Entity(
|
||||
grid_pos=(8, 6),
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84
|
||||
)
|
||||
grid.entities.append(entity)
|
||||
|
||||
# Animate the draw position for smooth movement
|
||||
offset = 0.0
|
||||
direction = 1
|
||||
|
||||
def animate_offset(timer, runtime):
|
||||
global offset, direction
|
||||
offset += 0.02 * direction
|
||||
if offset > 0.5:
|
||||
direction = -1
|
||||
elif offset < -0.5:
|
||||
direction = 1
|
||||
entity.draw_pos = mcrfpy.Vector(8 + offset, 6)
|
||||
|
||||
timer = mcrfpy.Timer("move", animate_offset, 16)
|
||||
|
||||
status = mcrfpy.Caption(text="draw_pos allows sub-tile positioning", pos=(340, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
38
tests/snippets/031_animation_basic.py
Normal file
38
tests/snippets/031_animation_basic.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,InputState,Key,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Animation Basic - Animate position
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Dark background
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Create a frame to animate
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334),
|
||||
size=(150, 100),
|
||||
fill_color=mcrfpy.Color(100, 150, 255)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(text="SPACE to restart animation", pos=(380, 700))
|
||||
label.outline = 2
|
||||
label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label)
|
||||
|
||||
# Animate position when space pressed
|
||||
def on_key(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
frame.x = 100
|
||||
frame.animate("x", 774, 2.0, mcrfpy.Easing.LINEAR)
|
||||
|
||||
scene.on_key = on_key
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.animate("x", 774, 2.0, mcrfpy.Easing.LINEAR)
|
||||
|
||||
# Auto-start animation with looping
|
||||
frame.animate("x", 774, 2.0, mcrfpy.Easing.LINEAR)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 7000)
|
||||
42
tests/snippets/032_animation_easing_linear.py
Normal file
42
tests/snippets/032_animation_easing_linear.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Easing: LINEAR - Constant speed
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Background
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Moving frame
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(255, 150, 100)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
# Start/end markers
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(100, 300), size=(5, 170),
|
||||
fill_color=mcrfpy.Color(100, 100, 100)
|
||||
))
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(824, 300), size=(5, 170),
|
||||
fill_color=mcrfpy.Color(100, 100, 100)
|
||||
))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="LINEAR - Constant velocity",
|
||||
pos=(380, 200))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.animate("x", 824, 3.0, mcrfpy.Easing.LINEAR)
|
||||
|
||||
frame.animate("x", 824, 3.0, mcrfpy.Easing.LINEAR)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 8000) # 3s animation + 5s pause
|
||||
34
tests/snippets/033_animation_easing_ease_in.py
Normal file
34
tests/snippets/033_animation_easing_ease_in.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Easing: EASE_IN - Slow start, fast end
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(100, 255, 150)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
# Markers
|
||||
scene.children.append(mcrfpy.Frame(pos=(100, 300), size=(5, 170), fill_color=mcrfpy.Color(100, 100, 100)))
|
||||
scene.children.append(mcrfpy.Frame(pos=(824, 300), size=(5, 170), fill_color=mcrfpy.Color(100, 100, 100)))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="EASE_IN - Accelerates from rest",
|
||||
pos=(350, 200))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.animate("x", 824, 3.0, mcrfpy.Easing.EASE_IN)
|
||||
|
||||
frame.animate("x", 824, 3.0, mcrfpy.Easing.EASE_IN)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 8000)
|
||||
34
tests/snippets/034_animation_easing_ease_out.py
Normal file
34
tests/snippets/034_animation_easing_ease_out.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Easing: EASE_OUT - Fast start, slow end
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(150, 100, 255)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
# Markers
|
||||
scene.children.append(mcrfpy.Frame(pos=(100, 300), size=(5, 170), fill_color=mcrfpy.Color(100, 100, 100)))
|
||||
scene.children.append(mcrfpy.Frame(pos=(824, 300), size=(5, 170), fill_color=mcrfpy.Color(100, 100, 100)))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="EASE_OUT - Decelerates to rest",
|
||||
pos=(350, 200))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.animate("x", 824, 3.0, mcrfpy.Easing.EASE_OUT)
|
||||
|
||||
frame.animate("x", 824, 3.0, mcrfpy.Easing.EASE_OUT)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 8000)
|
||||
34
tests/snippets/035_animation_easing_ease_in_out.py
Normal file
34
tests/snippets/035_animation_easing_ease_in_out.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Easing: EASE_IN_OUT - Smooth both ends
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(255, 200, 100)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
# Markers
|
||||
scene.children.append(mcrfpy.Frame(pos=(100, 300), size=(5, 170), fill_color=mcrfpy.Color(100, 100, 100)))
|
||||
scene.children.append(mcrfpy.Frame(pos=(824, 300), size=(5, 170), fill_color=mcrfpy.Color(100, 100, 100)))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="EASE_IN_OUT - Smooth acceleration & deceleration",
|
||||
pos=(300, 200))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.animate("x", 824, 3.0, mcrfpy.Easing.EASE_IN_OUT)
|
||||
|
||||
frame.animate("x", 824, 3.0, mcrfpy.Easing.EASE_IN_OUT)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 8000)
|
||||
37
tests/snippets/036_animation_easing_bounce.py
Normal file
37
tests/snippets/036_animation_easing_bounce.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Easing: BOUNCE - Bouncy effect
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Ball that bounces
|
||||
ball = mcrfpy.Frame(
|
||||
pos=(462, 100), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(255, 100, 100)
|
||||
)
|
||||
scene.children.append(ball)
|
||||
|
||||
# Ground line
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 600), size=(1024, 5),
|
||||
fill_color=mcrfpy.Color(100, 100, 100)
|
||||
))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="EASE_OUT_BOUNCE - Ball drop effect",
|
||||
pos=(340, 680))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
ball.y = 100
|
||||
ball.animate("y", 500, 2.0, mcrfpy.Easing.EASE_OUT_BOUNCE)
|
||||
|
||||
ball.animate("y", 500, 2.0, mcrfpy.Easing.EASE_OUT_BOUNCE)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 7000) # 2s animation + 5s pause
|
||||
36
tests/snippets/037_animation_easing_elastic.py
Normal file
36
tests/snippets/037_animation_easing_elastic.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Easing: ELASTIC - Springy overshoot
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(100, 200, 255)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
# Target marker
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(824, 300), size=(5, 170),
|
||||
fill_color=mcrfpy.Color(255, 100, 100)
|
||||
))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="EASE_OUT_ELASTIC - Spring overshoot",
|
||||
pos=(340, 200))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.animate("x", 824, 2.5, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
|
||||
frame.animate("x", 824, 2.5, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 7500) # 2.5s animation + 5s pause
|
||||
34
tests/snippets/038_animation_easing_back.py
Normal file
34
tests/snippets/038_animation_easing_back.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Easing: BACK - Anticipation/overshoot
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(200, 150, 255)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
# Markers
|
||||
scene.children.append(mcrfpy.Frame(pos=(100, 300), size=(5, 170), fill_color=mcrfpy.Color(100, 100, 100)))
|
||||
scene.children.append(mcrfpy.Frame(pos=(824, 300), size=(5, 170), fill_color=mcrfpy.Color(100, 100, 100)))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="EASE_IN_OUT_BACK - Pull back, then overshoot",
|
||||
pos=(300, 200))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.animate("x", 824, 2.0, mcrfpy.Easing.EASE_IN_OUT_BACK)
|
||||
|
||||
frame.animate("x", 824, 2.0, mcrfpy.Easing.EASE_IN_OUT_BACK)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 7000) # 2s animation + 5s pause
|
||||
43
tests/snippets/039_animation_easing_comparison.py
Normal file
43
tests/snippets/039_animation_easing_comparison.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Easing Comparison - Multiple easings side by side
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
easings = [
|
||||
(mcrfpy.Easing.LINEAR, "LINEAR", mcrfpy.Color(255, 100, 100)),
|
||||
(mcrfpy.Easing.EASE_IN_QUAD, "EASE_IN_QUAD", mcrfpy.Color(255, 200, 100)),
|
||||
(mcrfpy.Easing.EASE_OUT_QUAD, "EASE_OUT_QUAD", mcrfpy.Color(200, 255, 100)),
|
||||
(mcrfpy.Easing.EASE_IN_OUT_QUAD, "EASE_IN_OUT_QUAD", mcrfpy.Color(100, 255, 100)),
|
||||
(mcrfpy.Easing.EASE_OUT_BOUNCE, "EASE_OUT_BOUNCE", mcrfpy.Color(100, 255, 200)),
|
||||
(mcrfpy.Easing.EASE_OUT_ELASTIC, "EASE_OUT_ELASTIC", mcrfpy.Color(100, 200, 255)),
|
||||
]
|
||||
|
||||
frames = []
|
||||
for i, (easing, name, color) in enumerate(easings):
|
||||
y = 80 + i * 110
|
||||
|
||||
label = mcrfpy.Caption(text=name, pos=(100, y + 15))
|
||||
label.fill_color = mcrfpy.Color(180, 180, 180)
|
||||
scene.children.append(label)
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(350, y), size=(60, 50),
|
||||
fill_color=color
|
||||
)
|
||||
scene.children.append(frame)
|
||||
frames.append((frame, easing))
|
||||
frame.animate("x", 900, 3.0, easing)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
for frame, easing in frames:
|
||||
frame.x = 350
|
||||
frame.animate("x", 900, 3.0, easing)
|
||||
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 8000)
|
||||
34
tests/snippets/040_animation_color.py
Normal file
34
tests/snippets/040_animation_color.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Animation Color - Animate fill_color
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Large frame to show color change
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(262, 184), size=(500, 400),
|
||||
fill_color=mcrfpy.Color(255, 100, 100),
|
||||
outline=3.0,
|
||||
outline_color=mcrfpy.Color(255, 255, 255)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="Animating fill_color from red to blue",
|
||||
pos=(340, 620))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.fill_color = mcrfpy.Color(255, 100, 100)
|
||||
frame.animate("fill_color", (100, 100, 255, 255), 3.0, mcrfpy.Easing.EASE_IN_OUT)
|
||||
|
||||
# Animate to blue (pass color as RGBA tuple)
|
||||
frame.animate("fill_color", (100, 100, 255, 255), 3.0, mcrfpy.Easing.EASE_IN_OUT)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 8000)
|
||||
38
tests/snippets/041_animation_opacity.py
Normal file
38
tests/snippets/041_animation_opacity.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Animation Opacity - Fade in/out
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Frame that fades out
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(262, 184), size=(500, 400),
|
||||
fill_color=mcrfpy.Color(200, 100, 255),
|
||||
opacity=1.0
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(
|
||||
text="I'm fading away...",
|
||||
pos=(170, 180))
|
||||
label.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
frame.children.append(label)
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="Animating opacity from 1.0 to 0.0",
|
||||
pos=(360, 620))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.opacity = 1.0
|
||||
frame.animate("opacity", 0.0, 4.0, mcrfpy.Easing.EASE_IN)
|
||||
|
||||
frame.animate("opacity", 0.0, 4.0, mcrfpy.Easing.EASE_IN)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 9000) # 4s animation + 5s pause
|
||||
37
tests/snippets/042_animation_size.py
Normal file
37
tests/snippets/042_animation_size.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Animation Size - Grow and shrink
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Frame that grows
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(462, 334), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(100, 255, 150),
|
||||
outline=2.0,
|
||||
outline_color=mcrfpy.Color(255, 255, 255)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="Animating w and h properties",
|
||||
pos=(380, 620))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.w = 100
|
||||
frame.h = 100
|
||||
frame.animate("w", 400, 2.0, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
frame.animate("h", 300, 2.0, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
|
||||
# Animate both dimensions
|
||||
frame.animate("w", 400, 2.0, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
frame.animate("h", 300, 2.0, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 7000) # 2s animation + 5s pause
|
||||
34
tests/snippets/043_animation_sprite_index.py
Normal file
34
tests/snippets/043_animation_sprite_index.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Sprite,Timer] verified=0.2.8-dev status=ok
|
||||
# Animation Sprite Index - Sprite animation
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Large sprite for visibility
|
||||
sprite = mcrfpy.Sprite(
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84,
|
||||
pos=(400, 250),
|
||||
scale=12.0
|
||||
)
|
||||
scene.children.append(sprite)
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="Animating sprite_index (84 -> 120)",
|
||||
pos=(360, 600))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
sprite.sprite_index = 84
|
||||
sprite.animate("sprite_index", 120, 3.0, mcrfpy.Easing.LINEAR)
|
||||
|
||||
# Animate through sprite indices
|
||||
sprite.animate("sprite_index", 120, 3.0, mcrfpy.Easing.LINEAR)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 8000)
|
||||
37
tests/snippets/044_animation_callback.py
Normal file
37
tests/snippets/044_animation_callback.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Animation Callback - Run code when animation ends
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(255, 150, 100)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
status = mcrfpy.Caption(
|
||||
text="Animation in progress...",
|
||||
pos=(380, 500))
|
||||
status.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
scene.children.append(status)
|
||||
|
||||
# Callback when animation completes
|
||||
def on_complete(target, prop, value):
|
||||
status.text = f"Animation complete! {prop}={value:.0f}"
|
||||
target.fill_color = mcrfpy.Color(100, 255, 100)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.fill_color = mcrfpy.Color(255, 150, 100)
|
||||
status.text = "Animation in progress..."
|
||||
frame.animate("x", 824, 2.0, mcrfpy.Easing.EASE_OUT, callback=on_complete)
|
||||
|
||||
frame.animate("x", 824, 2.0, mcrfpy.Easing.EASE_OUT, callback=on_complete)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 7000) # 2s animation + 5s pause
|
||||
46
tests/snippets/045_animation_chain.py
Normal file
46
tests/snippets/045_animation_chain.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Animation Chain - Sequential animations
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 100), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(255, 100, 100)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
status = mcrfpy.Caption(text="Step 1: Moving right", pos=(380, 620))
|
||||
scene.children.append(status)
|
||||
|
||||
def step2(target, prop, value):
|
||||
status.text = "Step 2: Moving down"
|
||||
target.fill_color = mcrfpy.Color(100, 255, 100)
|
||||
target.animate("y", 568, 1.0, mcrfpy.Easing.EASE_IN_OUT, callback=step3)
|
||||
|
||||
def step3(target, prop, value):
|
||||
status.text = "Step 3: Moving left"
|
||||
target.fill_color = mcrfpy.Color(100, 100, 255)
|
||||
target.animate("x", 100, 1.0, mcrfpy.Easing.EASE_IN_OUT, callback=step4)
|
||||
|
||||
def step4(target, prop, value):
|
||||
status.text = "Step 4: Moving up - Complete!"
|
||||
target.fill_color = mcrfpy.Color(255, 255, 100)
|
||||
target.animate("y", 100, 1.0, mcrfpy.Easing.EASE_IN_OUT)
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.y = 100
|
||||
frame.fill_color = mcrfpy.Color(255, 100, 100)
|
||||
status.text = "Step 1: Moving right"
|
||||
frame.animate("x", 824, 1.0, mcrfpy.Easing.EASE_IN_OUT, callback=step2)
|
||||
|
||||
# Start chain
|
||||
frame.animate("x", 824, 1.0, mcrfpy.Easing.EASE_IN_OUT, callback=step2)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 9000) # 4s animation + 5s pause
|
||||
32
tests/snippets/046_timer_basic.py
Normal file
32
tests/snippets/046_timer_basic.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Timer Basic - Periodic callback
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
counter = mcrfpy.Caption(
|
||||
text="Ticks: 0",
|
||||
pos=(420, 350))
|
||||
counter.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(counter)
|
||||
|
||||
count = 0
|
||||
|
||||
def on_tick(timer, runtime):
|
||||
global count
|
||||
count += 1
|
||||
counter.text = f"Ticks: {count}"
|
||||
|
||||
# Timer fires every 500ms
|
||||
timer = mcrfpy.Timer("ticker", on_tick, 500)
|
||||
|
||||
scene.children.append(mcrfpy.Caption(
|
||||
text="Timer interval: 500ms",
|
||||
pos=(410, 450)
|
||||
))
|
||||
29
tests/snippets/047_timer_once.py
Normal file
29
tests/snippets/047_timer_once.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Timer Once - Single-shot timer
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
status = mcrfpy.Caption(
|
||||
text="Waiting for timer...",
|
||||
pos=(380, 350))
|
||||
status.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
scene.children.append(status)
|
||||
|
||||
def on_fire(timer, runtime):
|
||||
status.text = "Timer fired! (once=True)"
|
||||
status.fill_color = mcrfpy.Color(100, 255, 100)
|
||||
|
||||
# Timer fires once after 2 seconds
|
||||
timer = mcrfpy.Timer("delayed", on_fire, 2000, once=True)
|
||||
|
||||
scene.children.append(mcrfpy.Caption(
|
||||
text="Single-shot timer fires after 2 seconds",
|
||||
pos=(320, 450)
|
||||
))
|
||||
51
tests/snippets/048_timer_control.py
Normal file
51
tests/snippets/048_timer_control.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Key,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Timer Control - Pause, resume, stop
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Moving frame
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 334), size=(80, 80),
|
||||
fill_color=mcrfpy.Color(255, 150, 100)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
status = mcrfpy.Caption(
|
||||
text="Timer running - P=pause, R=resume, S=stop",
|
||||
pos=(280, 500)
|
||||
)
|
||||
scene.children.append(status)
|
||||
|
||||
direction = 1
|
||||
|
||||
def animate(timer, runtime):
|
||||
global direction
|
||||
frame.x += 5 * direction
|
||||
if frame.x > 844:
|
||||
direction = -1
|
||||
elif frame.x < 100:
|
||||
direction = 1
|
||||
|
||||
timer = mcrfpy.Timer("mover", animate, 16)
|
||||
|
||||
def on_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED:
|
||||
return
|
||||
if key == mcrfpy.Key.P:
|
||||
timer.pause()
|
||||
status.text = "Timer PAUSED"
|
||||
elif key == mcrfpy.Key.R:
|
||||
timer.resume()
|
||||
status.text = "Timer RESUMED"
|
||||
elif key == mcrfpy.Key.S:
|
||||
timer.stop()
|
||||
status.text = "Timer STOPPED"
|
||||
|
||||
scene.on_key = on_key
|
||||
38
tests/snippets/049_input_keyboard.py
Normal file
38
tests/snippets/049_input_keyboard.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Scene] verified=0.2.8-dev status=ok
|
||||
# Input Keyboard - Key press handling
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
key_display = mcrfpy.Caption(
|
||||
text="Press any key...",
|
||||
pos=(380, 300))
|
||||
key_display.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(key_display)
|
||||
|
||||
state_display = mcrfpy.Caption(
|
||||
text="State: waiting",
|
||||
pos=(420, 400))
|
||||
state_display.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
scene.children.append(state_display)
|
||||
|
||||
def on_key(key, action):
|
||||
key_display.text = f"Key: {key}"
|
||||
|
||||
if action == mcrfpy.InputState.PRESSED:
|
||||
state_display.text = "State: PRESSED"
|
||||
state_display.fill_color = mcrfpy.Color(100, 255, 100)
|
||||
elif action == mcrfpy.InputState.RELEASED:
|
||||
state_display.text = "State: RELEASED"
|
||||
state_display.fill_color = mcrfpy.Color(255, 100, 100)
|
||||
elif action == mcrfpy.InputState.HELD:
|
||||
state_display.text = "State: HELD"
|
||||
state_display.fill_color = mcrfpy.Color(255, 255, 100)
|
||||
|
||||
scene.on_key = on_key
|
||||
41
tests/snippets/050_input_mouse_click.py
Normal file
41
tests/snippets/050_input_mouse_click.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Scene] verified=0.2.8-dev status=ok
|
||||
# Input Mouse Click - Click on frames
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Clickable button
|
||||
button = mcrfpy.Frame(
|
||||
pos=(362, 284), size=(300, 200),
|
||||
fill_color=mcrfpy.Color(100, 100, 200),
|
||||
outline=3.0,
|
||||
outline_color=mcrfpy.Color(150, 150, 255)
|
||||
)
|
||||
scene.children.append(button)
|
||||
|
||||
label = mcrfpy.Caption(
|
||||
text="Click me!",
|
||||
pos=(100, 80)
|
||||
)
|
||||
button.children.append(label)
|
||||
|
||||
click_count = 0
|
||||
status = mcrfpy.Caption(text="Clicks: 0", pos=(450, 550))
|
||||
scene.children.append(status)
|
||||
|
||||
def on_click(pos, button_type, action):
|
||||
global click_count
|
||||
if action == mcrfpy.InputState.PRESSED:
|
||||
click_count += 1
|
||||
status.text = f"Clicks: {click_count}"
|
||||
button.fill_color = mcrfpy.Color(150, 150, 255)
|
||||
elif action == mcrfpy.InputState.RELEASED:
|
||||
button.fill_color = mcrfpy.Color(100, 100, 200)
|
||||
|
||||
button.on_click = on_click
|
||||
45
tests/snippets/051_input_mouse_hover.py
Normal file
45
tests/snippets/051_input_mouse_hover.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Input Mouse Hover - Enter/exit/move events
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
))
|
||||
|
||||
# Hoverable frame
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(312, 234), size=(400, 300),
|
||||
fill_color=mcrfpy.Color(80, 80, 120),
|
||||
outline=3.0,
|
||||
outline_color=mcrfpy.Color(120, 120, 180)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
status = mcrfpy.Caption(
|
||||
text="Mouse outside",
|
||||
pos=(140, 130)
|
||||
)
|
||||
frame.children.append(status)
|
||||
|
||||
coords = mcrfpy.Caption(text="", pos=(140, 180))
|
||||
frame.children.append(coords)
|
||||
|
||||
def on_enter(pos):
|
||||
frame.fill_color = mcrfpy.Color(100, 150, 100)
|
||||
status.text = "Mouse ENTERED"
|
||||
|
||||
def on_exit(pos):
|
||||
frame.fill_color = mcrfpy.Color(80, 80, 120)
|
||||
status.text = "Mouse EXITED"
|
||||
coords.text = ""
|
||||
|
||||
def on_move(pos):
|
||||
coords.text = f"Position: ({pos.x:.0f}, {pos.y:.0f})"
|
||||
|
||||
frame.on_enter = on_enter
|
||||
frame.on_exit = on_exit
|
||||
frame.on_move = on_move
|
||||
47
tests/snippets/052_input_grid_cell_click.py
Normal file
47
tests/snippets/052_input_grid_cell_click.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Grid,InputState,Scene] verified=0.2.8-dev status=ok
|
||||
# Input Grid Cell Click - Click on grid cells
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add color layer for click highlighting
|
||||
click_layer = mcrfpy.ColorLayer(z_index=1, name="clicks")
|
||||
grid.add_layer(click_layer)
|
||||
|
||||
# Fill with floor
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
status = mcrfpy.Caption(text="Click on cells to highlight", pos=(350, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
|
||||
# Track clicked cells
|
||||
clicked = set()
|
||||
|
||||
def on_cell_click(cell_pos, button, action):
|
||||
if action == mcrfpy.InputState.PRESSED:
|
||||
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||
if (x, y) in clicked:
|
||||
# Clear highlight
|
||||
click_layer.set((x, y), mcrfpy.Color(0, 0, 0, 0))
|
||||
clicked.discard((x, y))
|
||||
else:
|
||||
# Add highlight
|
||||
click_layer.set((x, y), mcrfpy.Color(255, 200, 100, 180))
|
||||
clicked.add((x, y))
|
||||
status.text = f"Clicked cell ({x}, {y})"
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
40
tests/snippets/053_input_grid_cell_hover.py
Normal file
40
tests/snippets/053_input_grid_cell_hover.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Input Grid Cell Hover - Hover over grid cells
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add color layer for hover highlighting
|
||||
hover_layer = mcrfpy.ColorLayer(z_index=1, name="hover")
|
||||
grid.add_layer(hover_layer)
|
||||
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
status = mcrfpy.Caption(text="Hover over cells", pos=(420, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
|
||||
def on_cell_enter(cell_pos):
|
||||
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||
hover_layer.set((x, y), mcrfpy.Color(100, 200, 255, 180))
|
||||
status.text = f"Hovering cell ({x}, {y})"
|
||||
|
||||
def on_cell_exit(cell_pos):
|
||||
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||
hover_layer.set((x, y), mcrfpy.Color(0, 0, 0, 0))
|
||||
|
||||
grid.on_cell_enter = on_cell_enter
|
||||
grid.on_cell_exit = on_cell_exit
|
||||
56
tests/snippets/054_pathfinding_astar.py
Normal file
56
tests/snippets/054_pathfinding_astar.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Pathfinding A* - Find path between points
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add color layer for path visualization
|
||||
path_layer = mcrfpy.ColorLayer(z_index=1, name="path")
|
||||
grid.add_layer(path_layer)
|
||||
|
||||
# Create maze
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
# Border walls
|
||||
if x == 0 or x == 15 or y == 0 or y == 11:
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
# Some obstacles
|
||||
elif x == 8 and y > 1 and y < 10:
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
elif x == 4 and y > 3 and y < 11:
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
cell.walkable = True
|
||||
|
||||
# Find path
|
||||
start = (2, 6)
|
||||
end = (13, 6)
|
||||
path = grid.find_path(start, end)
|
||||
|
||||
# Highlight path using color layer
|
||||
for px, py in path:
|
||||
path_layer.set((px, py), mcrfpy.Color(100, 255, 100, 180))
|
||||
|
||||
# Mark start/end
|
||||
path_layer.set((start[0], start[1]), mcrfpy.Color(100, 100, 255, 200))
|
||||
path_layer.set((end[0], end[1]), mcrfpy.Color(255, 100, 100, 200))
|
||||
|
||||
status = mcrfpy.Caption(text=f"A* Path length: {len(path)} - Blue=start, Red=end", pos=(320, 700))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
54
tests/snippets/055_pathfinding_entity.py
Normal file
54
tests/snippets/055_pathfinding_entity.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Entity,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Entity Pathfinding - Entity path_to method
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add color layer for path visualization
|
||||
path_layer = mcrfpy.ColorLayer(z_index=1, name="path")
|
||||
grid.add_layer(path_layer)
|
||||
|
||||
# Floor with some walls
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
if x == 8 and y > 1 and y < 10:
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
cell.walkable = True
|
||||
|
||||
# Entity
|
||||
player = mcrfpy.Entity(
|
||||
grid_pos=(3, 6),
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84
|
||||
)
|
||||
grid.entities.append(player)
|
||||
|
||||
# Find path to target
|
||||
target = (13, 6)
|
||||
path = player.path_to(target)
|
||||
|
||||
# Visualize path using color layer
|
||||
for px, py in path:
|
||||
path_layer.set((px, py), mcrfpy.Color(150, 200, 255, 180))
|
||||
|
||||
# Mark target
|
||||
path_layer.set((target[0], target[1]), mcrfpy.Color(255, 200, 100, 200))
|
||||
|
||||
status = mcrfpy.Caption(text="Entity pathfinding around wall", pos=(360, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
52
tests/snippets/056_pathfinding_dijkstra.py
Normal file
52
tests/snippets/056_pathfinding_dijkstra.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Dijkstra Map - Distance-based pathfinding
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add color layer for visualization
|
||||
dist_layer = mcrfpy.ColorLayer(z_index=1, name="distances")
|
||||
grid.add_layer(dist_layer)
|
||||
|
||||
# Floor tiles
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
cell.tilesprite = 48
|
||||
cell.walkable = True
|
||||
|
||||
# Some walls
|
||||
for i in range(5):
|
||||
grid.at(8, 2 + i).tilesprite = 1
|
||||
grid.at(8, 2 + i).walkable = False
|
||||
|
||||
# Compute dijkstra from center
|
||||
center = (8, 6)
|
||||
dijkstra = grid.get_dijkstra_map(center)
|
||||
|
||||
# Visualize distances with color gradient
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
if grid.at(x, y).walkable:
|
||||
dist = dijkstra.distance((x, y))
|
||||
if dist is not None and dist < 1000: # Valid distance
|
||||
intensity = max(0, 255 - int(dist * 20))
|
||||
dist_layer.set((x, y), mcrfpy.Color(intensity, intensity, 255, 180))
|
||||
|
||||
# Mark center
|
||||
dist_layer.set((center[0], center[1]), mcrfpy.Color(255, 100, 100, 255))
|
||||
|
||||
status = mcrfpy.Caption(text="Dijkstra distance map (darker = farther)", pos=(320, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
58
tests/snippets/057_fov_basic.py
Normal file
58
tests/snippets/057_fov_basic.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Entity,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# FOV Basic - Field of view computation
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add fog layer for FOV visualization
|
||||
fog_layer = mcrfpy.ColorLayer(z_index=1, name="fog")
|
||||
grid.add_layer(fog_layer)
|
||||
fog_layer.fill(mcrfpy.Color(0, 0, 0, 200)) # Start dark
|
||||
|
||||
# Create dungeon with walls
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
# Border walls
|
||||
if x == 0 or x == 15 or y == 0 or y == 11:
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
cell.transparent = False
|
||||
# Internal pillars
|
||||
elif (x in [4, 8, 12]) and (y in [4, 8]):
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
cell.transparent = False
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
cell.walkable = True
|
||||
cell.transparent = True
|
||||
|
||||
# Player position
|
||||
player_pos = (8, 6)
|
||||
player = mcrfpy.Entity(grid_pos=player_pos, texture=mcrfpy.default_texture, sprite_index=84)
|
||||
grid.entities.append(player)
|
||||
|
||||
# Compute FOV from player position
|
||||
grid.compute_fov(player_pos, radius=8)
|
||||
|
||||
# Clear fog for visible cells
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
if grid.is_in_fov((x, y)):
|
||||
fog_layer.set((x, y), mcrfpy.Color(0, 0, 0, 0)) # Clear
|
||||
|
||||
status = mcrfpy.Caption(text="FOV radius=8, dark areas not visible", pos=(340, 700))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
60
tests/snippets/058_fov_algorithms.py
Normal file
60
tests/snippets/058_fov_algorithms.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,FOV,Frame,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# FOV Algorithms - Compare different algorithms
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
algorithms = [
|
||||
(mcrfpy.FOV.BASIC, "BASIC"),
|
||||
(mcrfpy.FOV.SHADOW, "SHADOW"),
|
||||
(mcrfpy.FOV.DIAMOND, "DIAMOND"),
|
||||
(mcrfpy.FOV.PERMISSIVE_4, "PERMISSIVE4"),
|
||||
]
|
||||
|
||||
for i, (algo, name) in enumerate(algorithms):
|
||||
x = 62 + (i % 2) * 480
|
||||
y = 60 + (i // 2) * 360
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(12, 9),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(x, y),
|
||||
size=(420, 300)
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add fog layer
|
||||
fog = mcrfpy.ColorLayer(z_index=1, name="fog")
|
||||
grid.add_layer(fog)
|
||||
fog.fill(mcrfpy.Color(0, 0, 0, 180))
|
||||
|
||||
# Setup dungeon
|
||||
for gy in range(9):
|
||||
for gx in range(12):
|
||||
cell = grid.at(gx, gy)
|
||||
if gx == 0 or gx == 11 or gy == 0 or gy == 8:
|
||||
cell.tilesprite = 1
|
||||
cell.transparent = False
|
||||
elif gx == 6 and gy > 1 and gy < 7:
|
||||
cell.tilesprite = 1
|
||||
cell.transparent = False
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
cell.transparent = True
|
||||
|
||||
# Compute FOV with this algorithm
|
||||
grid.compute_fov((3, 4), radius=6, algorithm=algo)
|
||||
|
||||
# Clear fog for visible cells
|
||||
for gy in range(9):
|
||||
for gx in range(12):
|
||||
if grid.is_in_fov((gx, gy)):
|
||||
fog.set((gx, gy), mcrfpy.Color(0, 0, 0, 0))
|
||||
|
||||
label = mcrfpy.Caption(text=name, pos=(x + 170, y + 310))
|
||||
label.outline = 2
|
||||
label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label)
|
||||
53
tests/snippets/059_fov_perspective.py
Normal file
53
tests/snippets/059_fov_perspective.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,InputState,Key,Scene] verified=0.2.8-dev status=ok
|
||||
# FOV Perspective Entity - View from entity's perspective
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Dungeon layout
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
if x == 0 or x == 15 or y == 0 or y == 11:
|
||||
cell.tilesprite = 1
|
||||
cell.transparent = False
|
||||
elif (x == 4 or x == 12) and y > 2 and y < 9:
|
||||
cell.tilesprite = 1
|
||||
cell.transparent = False
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
cell.transparent = True
|
||||
|
||||
# Player entity
|
||||
player = mcrfpy.Entity(grid_pos=(8, 6), texture=mcrfpy.default_texture, sprite_index=84)
|
||||
grid.entities.append(player)
|
||||
|
||||
# Set perspective entity - FOV follows this entity
|
||||
grid.perspective = player
|
||||
grid.fov_radius = 6
|
||||
|
||||
status = mcrfpy.Caption(text="WASD to move, FOV follows player", pos=(350, 700))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
|
||||
def on_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED:
|
||||
return
|
||||
x, y = player.grid_x, player.grid_y
|
||||
if key == mcrfpy.Key.W: player.grid_pos = (x, y - 1)
|
||||
elif key == mcrfpy.Key.S: player.grid_pos = (x, y + 1)
|
||||
elif key == mcrfpy.Key.A: player.grid_pos = (x - 1, y)
|
||||
elif key == mcrfpy.Key.D: player.grid_pos = (x + 1, y)
|
||||
|
||||
scene.on_key = on_key
|
||||
56
tests/snippets/060_bsp_basic.py
Normal file
56
tests/snippets/060_bsp_basic.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# mcrf: objects=[BSP,Caption,Color,ColorLayer,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# BSP Basic - Binary space partition for dungeon generation
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add color layer for room visualization
|
||||
room_layer = mcrfpy.ColorLayer(z_index=1, name="rooms")
|
||||
grid.add_layer(room_layer)
|
||||
|
||||
# Fill with walls
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 1
|
||||
grid.at(x, y).walkable = False
|
||||
|
||||
# Create BSP tree (pos and size as tuples)
|
||||
bsp = mcrfpy.BSP(pos=(0, 0), size=(16, 12))
|
||||
bsp.split_recursive(depth=3, min_size=(3, 3))
|
||||
|
||||
# Room colors
|
||||
colors = [
|
||||
mcrfpy.Color(255, 150, 150, 150),
|
||||
mcrfpy.Color(150, 255, 150, 150),
|
||||
mcrfpy.Color(150, 150, 255, 150),
|
||||
mcrfpy.Color(255, 255, 150, 150),
|
||||
mcrfpy.Color(255, 150, 255, 150),
|
||||
mcrfpy.Color(150, 255, 255, 150),
|
||||
]
|
||||
|
||||
# Carve rooms from leaves
|
||||
for i, leaf in enumerate(bsp):
|
||||
x, y = leaf.pos
|
||||
w, h = leaf.size
|
||||
# Shrink room slightly
|
||||
for ry in range(y + 1, y + h - 1):
|
||||
for rx in range(x + 1, x + w - 1):
|
||||
if 0 <= rx < 16 and 0 <= ry < 12:
|
||||
grid.at(rx, ry).tilesprite = 48
|
||||
grid.at(rx, ry).walkable = True
|
||||
room_layer.set((rx, ry), colors[i % len(colors)])
|
||||
|
||||
status = mcrfpy.Caption(text="BSP dungeon - each color is a leaf node", pos=(320, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
34
tests/snippets/061_scene_switching.py
Normal file
34
tests/snippets/061_scene_switching.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Key,Scene] verified=0.2.8-dev status=ok
|
||||
# Scene Switching - Multiple scenes
|
||||
import mcrfpy
|
||||
|
||||
# Create two scenes
|
||||
scene1 = mcrfpy.Scene("menu")
|
||||
scene2 = mcrfpy.Scene("game")
|
||||
|
||||
# Menu scene
|
||||
scene1.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(50, 50, 80)))
|
||||
_caption = mcrfpy.Caption(text="MENU SCENE", pos=(420, 300))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene1.children.append(_caption)
|
||||
scene1.children.append(mcrfpy.Caption(text="Press SPACE to go to game", pos=(360, 400)))
|
||||
|
||||
# Game scene
|
||||
scene2.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(80, 50, 50)))
|
||||
_caption = mcrfpy.Caption(text="GAME SCENE", pos=(420, 300))
|
||||
_caption.fill_color = mcrfpy.Color(100, 255, 100)
|
||||
scene2.children.append(_caption)
|
||||
scene2.children.append(mcrfpy.Caption(text="Press SPACE to go to menu", pos=(360, 400)))
|
||||
|
||||
def menu_key(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
scene2.activate()
|
||||
|
||||
def game_key(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
scene1.activate()
|
||||
|
||||
scene1.on_key = menu_key
|
||||
scene2.on_key = game_key
|
||||
|
||||
mcrfpy.current_scene = scene1
|
||||
36
tests/snippets/062_scene_transition_fade.py
Normal file
36
tests/snippets/062_scene_transition_fade.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Key,Scene,Transition] verified=0.2.8-dev status=ok
|
||||
# Scene Transition Fade - Smooth scene transitions
|
||||
import mcrfpy
|
||||
|
||||
scene1 = mcrfpy.Scene("scene1")
|
||||
scene2 = mcrfpy.Scene("scene2")
|
||||
|
||||
scene1.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(100, 50, 50)))
|
||||
_caption = mcrfpy.Caption(text="Scene 1 - Red", pos=(420, 350))
|
||||
_caption.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
scene1.children.append(_caption)
|
||||
|
||||
scene2.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(50, 50, 100)))
|
||||
_caption = mcrfpy.Caption(text="Scene 2 - Blue", pos=(420, 350))
|
||||
_caption.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
scene2.children.append(_caption)
|
||||
|
||||
# Set default transition
|
||||
mcrfpy.default_transition = mcrfpy.Transition.FADE
|
||||
mcrfpy.default_transition_duration = 1.0
|
||||
|
||||
scene1.children.append(mcrfpy.Caption(text="Press SPACE - Fade transition", pos=(340, 450)))
|
||||
scene2.children.append(mcrfpy.Caption(text="Press SPACE - Fade transition", pos=(340, 450)))
|
||||
|
||||
def key1(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
scene2.activate()
|
||||
|
||||
def key2(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
scene1.activate()
|
||||
|
||||
scene1.on_key = key1
|
||||
scene2.on_key = key2
|
||||
|
||||
mcrfpy.current_scene = scene1
|
||||
35
tests/snippets/063_scene_transition_slide.py
Normal file
35
tests/snippets/063_scene_transition_slide.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Key,Scene,Transition] verified=0.2.8-dev status=ok
|
||||
# Scene Transition Slide - Sliding transitions
|
||||
import mcrfpy
|
||||
|
||||
scene1 = mcrfpy.Scene("left")
|
||||
scene2 = mcrfpy.Scene("right")
|
||||
|
||||
scene1.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(50, 100, 50)))
|
||||
_caption = mcrfpy.Caption(text="LEFT Scene", pos=(420, 350))
|
||||
_caption.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
scene1.children.append(_caption)
|
||||
|
||||
scene2.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(100, 50, 100)))
|
||||
_caption = mcrfpy.Caption(text="RIGHT Scene", pos=(420, 350))
|
||||
_caption.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
scene2.children.append(_caption)
|
||||
|
||||
mcrfpy.default_transition = mcrfpy.Transition.SLIDE_LEFT
|
||||
mcrfpy.default_transition_duration = 0.5
|
||||
|
||||
scene1.children.append(mcrfpy.Caption(text="Press SPACE - Slide left", pos=(360, 450)))
|
||||
scene2.children.append(mcrfpy.Caption(text="Press SPACE - Slide left", pos=(360, 450)))
|
||||
|
||||
def key1(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
scene2.activate()
|
||||
|
||||
def key2(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
scene1.activate()
|
||||
|
||||
scene1.on_key = key1
|
||||
scene2.on_key = key2
|
||||
|
||||
mcrfpy.current_scene = scene1
|
||||
50
tests/snippets/064_vector_basics.py
Normal file
50
tests/snippets/064_vector_basics.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Vector] verified=0.2.8-dev status=ok
|
||||
# Vector Basics - 2D vector operations
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Create vectors
|
||||
v1 = mcrfpy.Vector(100, 50)
|
||||
v2 = mcrfpy.Vector(30, 80)
|
||||
|
||||
# Display vector info
|
||||
info = [
|
||||
f"v1 = Vector({v1.x}, {v1.y})",
|
||||
f"v2 = Vector({v2.x}, {v2.y})",
|
||||
f"",
|
||||
f"v1.magnitude() = {v1.magnitude():.2f}",
|
||||
f"v1.angle() = {v1.angle():.2f} radians",
|
||||
f"",
|
||||
f"Distance v1 to v2 = {v1.distance_to(v2):.2f}",
|
||||
]
|
||||
|
||||
y = 150
|
||||
for line in info:
|
||||
cap = mcrfpy.Caption(text=line, pos=(300, y))
|
||||
cap.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
scene.children.append(cap)
|
||||
y += 60
|
||||
|
||||
# Visual representation
|
||||
origin = mcrfpy.Frame(pos=(500, 500), size=(10, 10), fill_color=mcrfpy.Color(255, 255, 255))
|
||||
scene.children.append(origin)
|
||||
|
||||
# v1 endpoint
|
||||
p1 = mcrfpy.Frame(pos=(500 + v1.x * 2, 500 - v1.y * 2), size=(15, 15), fill_color=mcrfpy.Color(255, 100, 100))
|
||||
scene.children.append(p1)
|
||||
v1_label = mcrfpy.Caption(text="v1", pos=(500 + v1.x * 2 + 20, 500 - v1.y * 2))
|
||||
v1_label.outline = 2
|
||||
v1_label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(v1_label)
|
||||
|
||||
# v2 endpoint
|
||||
p2 = mcrfpy.Frame(pos=(500 + v2.x * 2, 500 - v2.y * 2), size=(15, 15), fill_color=mcrfpy.Color(100, 100, 255))
|
||||
scene.children.append(p2)
|
||||
v2_label = mcrfpy.Caption(text="v2", pos=(500 + v2.x * 2 + 20, 500 - v2.y * 2))
|
||||
v2_label.outline = 2
|
||||
v2_label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(v2_label)
|
||||
40
tests/snippets/065_color_components.py
Normal file
40
tests/snippets/065_color_components.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Color Components - RGBA color manipulation
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Show color components
|
||||
colors = [
|
||||
(mcrfpy.Color(255, 0, 0), "Pure Red"),
|
||||
(mcrfpy.Color(0, 255, 0), "Pure Green"),
|
||||
(mcrfpy.Color(0, 0, 255), "Pure Blue"),
|
||||
(mcrfpy.Color(255, 255, 0), "Yellow (R+G)"),
|
||||
(mcrfpy.Color(255, 0, 255), "Magenta (R+B)"),
|
||||
(mcrfpy.Color(0, 255, 255), "Cyan (G+B)"),
|
||||
(mcrfpy.Color(128, 128, 128), "Gray"),
|
||||
(mcrfpy.Color(255, 255, 255, 128), "White 50% Alpha"),
|
||||
]
|
||||
|
||||
for i, (color, name) in enumerate(colors):
|
||||
x = 100 + (i % 4) * 230
|
||||
y = 150 + (i // 4) * 280
|
||||
|
||||
frame = mcrfpy.Frame(pos=(x, y), size=(180, 100), fill_color=color)
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(text=name, pos=(x, y + 110))
|
||||
scene.children.append(label)
|
||||
|
||||
rgba = mcrfpy.Caption(
|
||||
text=f"R:{color.r} G:{color.g} B:{color.b} A:{color.a}",
|
||||
pos=(x, y + 140))
|
||||
rgba.fill_color = mcrfpy.Color(150, 150, 150)
|
||||
scene.children.append(rgba)
|
||||
|
||||
_caption = mcrfpy.Caption(text="Color RGBA Components", pos=(400, 60))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
36
tests/snippets/066_find_elements.py
Normal file
36
tests/snippets/066_find_elements.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Find Elements - Locate UI by name
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Create named elements
|
||||
elements = [
|
||||
("btn_play", 200, 200, mcrfpy.Color(100, 150, 100)),
|
||||
("btn_options", 200, 320, mcrfpy.Color(100, 100, 150)),
|
||||
("btn_quit", 200, 440, mcrfpy.Color(150, 100, 100)),
|
||||
("header", 500, 100, mcrfpy.Color(150, 150, 100)),
|
||||
]
|
||||
|
||||
for name, x, y, color in elements:
|
||||
frame = mcrfpy.Frame(pos=(x, y), size=(200, 80), fill_color=color, name=name)
|
||||
scene.children.append(frame)
|
||||
label = mcrfpy.Caption(text=name, pos=(20, 30))
|
||||
frame.children.append(label)
|
||||
|
||||
# Find specific element
|
||||
found = mcrfpy.find("btn_options")
|
||||
if found:
|
||||
found.outline = 4.0
|
||||
found.outline_color = mcrfpy.Color(255, 255, 255)
|
||||
|
||||
# Find all buttons
|
||||
buttons = mcrfpy.find_all("btn_*")
|
||||
status = mcrfpy.Caption(
|
||||
text=f"Found {len(buttons)} elements matching 'btn_*'",
|
||||
pos=(350, 600))
|
||||
status.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
scene.children.append(status)
|
||||
40
tests/snippets/067_collection_iteration.py
Normal file
40
tests/snippets/067_collection_iteration.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Collection Iteration - Loop through UI children
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Container with multiple children
|
||||
container = mcrfpy.Frame(pos=(162, 134), size=(700, 500), fill_color=mcrfpy.Color(50, 50, 70))
|
||||
scene.children.append(container)
|
||||
|
||||
# Add various elements
|
||||
for i in range(5):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(20 + i * 130, 50),
|
||||
size=(110, 80),
|
||||
fill_color=mcrfpy.Color(100 + i * 30, 100, 200 - i * 30)
|
||||
)
|
||||
container.children.append(frame)
|
||||
|
||||
for i in range(3):
|
||||
cap = mcrfpy.Caption(text=f"Label {i}", pos=(50 + i * 200, 200))
|
||||
container.children.append(cap)
|
||||
|
||||
# Iterate and count by type
|
||||
frames = 0
|
||||
captions = 0
|
||||
for child in container.children:
|
||||
if hasattr(child, 'fill_color'):
|
||||
frames += 1
|
||||
if hasattr(child, 'text'):
|
||||
captions += 1
|
||||
|
||||
status = mcrfpy.Caption(
|
||||
text=f"Container has {len(container.children)} children: {frames} frames, {captions} captions",
|
||||
pos=(100, 400)
|
||||
)
|
||||
container.children.append(status)
|
||||
41
tests/snippets/068_entity_collection.py
Normal file
41
tests/snippets/068_entity_collection.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Entity Collection - Manage grid entities
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
# Add multiple entities
|
||||
sprites = [84, 86, 88, 112, 116, 120]
|
||||
for i, sprite_idx in enumerate(sprites):
|
||||
entity = mcrfpy.Entity(
|
||||
grid_pos=(2 + i * 2, 6),
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=sprite_idx,
|
||||
name=f"entity_{i}"
|
||||
)
|
||||
grid.entities.append(entity)
|
||||
|
||||
# Count and iterate
|
||||
status = mcrfpy.Caption(text=f"Grid has {len(grid.entities)} entities", pos=(400, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
|
||||
# Highlight alternating entities
|
||||
for i, entity in enumerate(grid.entities):
|
||||
if i % 2 == 0:
|
||||
entity.opacity = 0.5
|
||||
33
tests/snippets/069_window_properties.py
Normal file
33
tests/snippets/069_window_properties.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Window Properties - Access window settings
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Display window info
|
||||
window = mcrfpy.window
|
||||
info = [
|
||||
f"Resolution: {window.resolution}",
|
||||
f"Game Resolution: {window.game_resolution}",
|
||||
f"Title: {window.title}",
|
||||
f"Fullscreen: {window.fullscreen}",
|
||||
f"VSync: {window.vsync}",
|
||||
f"Framerate Limit: {window.framerate_limit}",
|
||||
f"Scaling Mode: {window.scaling_mode}",
|
||||
]
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="Window Properties",
|
||||
pos=(400, 100))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
y = 200
|
||||
for line in info:
|
||||
cap = mcrfpy.Caption(text=line, pos=(300, y))
|
||||
cap.fill_color = mcrfpy.Color(200, 200, 200)
|
||||
scene.children.append(cap)
|
||||
y += 60
|
||||
46
tests/snippets/070_keyboard_state.py
Normal file
46
tests/snippets/070_keyboard_state.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Scene] verified=0.2.8-dev status=ok
|
||||
# Keyboard State - Track key presses
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
_caption = mcrfpy.Caption(text="Keyboard State", pos=(420, 100))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
last_key_label = mcrfpy.Caption(text="Last key: none", pos=(200, 250))
|
||||
scene.children.append(last_key_label)
|
||||
|
||||
state_label = mcrfpy.Caption(text="State: waiting", pos=(200, 350))
|
||||
scene.children.append(state_label)
|
||||
|
||||
modifier_label = mcrfpy.Caption(text="Modifiers: none", pos=(200, 450))
|
||||
scene.children.append(modifier_label)
|
||||
|
||||
scene.children.append(mcrfpy.Caption(text="Press keys to see state", pos=(350, 550)))
|
||||
|
||||
def on_key(key, action):
|
||||
last_key_label.text = f"Last key: {key}"
|
||||
|
||||
if action == mcrfpy.InputState.PRESSED:
|
||||
state_label.text = "State: PRESSED"
|
||||
state_label.fill_color = mcrfpy.Color(100, 255, 100)
|
||||
elif action == mcrfpy.InputState.RELEASED:
|
||||
state_label.text = "State: RELEASED"
|
||||
state_label.fill_color = mcrfpy.Color(255, 100, 100)
|
||||
elif action == mcrfpy.InputState.HELD:
|
||||
state_label.text = "State: HELD"
|
||||
state_label.fill_color = mcrfpy.Color(255, 255, 100)
|
||||
|
||||
# Show modifiers
|
||||
kb = mcrfpy.keyboard
|
||||
mods = []
|
||||
if kb.ctrl: mods.append("CTRL")
|
||||
if kb.shift: mods.append("SHIFT")
|
||||
if kb.alt: mods.append("ALT")
|
||||
modifier_label.text = f"Modifiers: {', '.join(mods) if mods else 'none'}"
|
||||
|
||||
scene.on_key = on_key
|
||||
39
tests/snippets/071_mouse_state.py
Normal file
39
tests/snippets/071_mouse_state.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Timer,Vector] verified=0.2.8-dev status=ok
|
||||
# Mouse State - Track mouse position and buttons
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
_caption = mcrfpy.Caption(text="Mouse State", pos=(430, 100))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
pos_label = mcrfpy.Caption(text="Position: (0, 0)", pos=(300, 250))
|
||||
scene.children.append(pos_label)
|
||||
|
||||
button_label = mcrfpy.Caption(text="Buttons: none", pos=(300, 350))
|
||||
scene.children.append(button_label)
|
||||
|
||||
# Cursor follower
|
||||
cursor = mcrfpy.Frame(pos=(0, 0), size=(20, 20), fill_color=mcrfpy.Color(255, 100, 100))
|
||||
scene.children.append(cursor)
|
||||
|
||||
def update_display(timer, runtime):
|
||||
mouse = mcrfpy.mouse
|
||||
pos = mouse.pos
|
||||
|
||||
pos_label.text = f"Position: ({pos.x:.0f}, {pos.y:.0f})"
|
||||
|
||||
# Check button states
|
||||
buttons = []
|
||||
if mouse.left: buttons.append("LEFT")
|
||||
if mouse.middle: buttons.append("MIDDLE")
|
||||
if mouse.right: buttons.append("RIGHT")
|
||||
button_label.text = f"Buttons: {', '.join(buttons) if buttons else 'none'}"
|
||||
|
||||
cursor.pos = mcrfpy.Vector(pos.x - 10, pos.y - 10)
|
||||
|
||||
timer = mcrfpy.Timer("mouse_poll", update_display, 16)
|
||||
47
tests/snippets/072_ui_button.py
Normal file
47
tests/snippets/072_ui_button.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Scene] verified=0.2.8-dev status=ok
|
||||
# UI Button - Interactive button with hover
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
def make_button(x, y, text, callback):
|
||||
btn = mcrfpy.Frame(
|
||||
pos=(x, y), size=(200, 60),
|
||||
fill_color=mcrfpy.Color(80, 80, 120),
|
||||
outline=2.0,
|
||||
outline_color=mcrfpy.Color(120, 120, 180)
|
||||
)
|
||||
label = mcrfpy.Caption(text=text, pos=(60, 18))
|
||||
btn.children.append(label)
|
||||
|
||||
def on_enter(pos):
|
||||
btn.fill_color = mcrfpy.Color(100, 100, 150)
|
||||
|
||||
def on_exit(pos):
|
||||
btn.fill_color = mcrfpy.Color(80, 80, 120)
|
||||
|
||||
def on_click(pos, button, action):
|
||||
if action == mcrfpy.InputState.PRESSED:
|
||||
btn.fill_color = mcrfpy.Color(60, 60, 100)
|
||||
callback()
|
||||
elif action == mcrfpy.InputState.RELEASED:
|
||||
btn.fill_color = mcrfpy.Color(100, 100, 150)
|
||||
|
||||
btn.on_enter = on_enter
|
||||
btn.on_exit = on_exit
|
||||
btn.on_click = on_click
|
||||
return btn
|
||||
|
||||
status = mcrfpy.Caption(text="Click a button", pos=(420, 600))
|
||||
scene.children.append(status)
|
||||
|
||||
def click1(): status.text = "Button 1 clicked!"
|
||||
def click2(): status.text = "Button 2 clicked!"
|
||||
def click3(): status.text = "Button 3 clicked!"
|
||||
|
||||
scene.children.append(make_button(412, 200, "Button 1", click1))
|
||||
scene.children.append(make_button(412, 300, "Button 2", click2))
|
||||
scene.children.append(make_button(412, 400, "Button 3", click3))
|
||||
64
tests/snippets/073_ui_health_bar.py
Normal file
64
tests/snippets/073_ui_health_bar.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,InputState,Key,Scene] verified=0.2.8-dev status=ok
|
||||
# UI Health Bar - Animated health display
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
class HealthBar:
|
||||
def __init__(self, x, y, width, height, max_health):
|
||||
self.max_health = max_health
|
||||
self.health = max_health
|
||||
self.width = width
|
||||
|
||||
# Background
|
||||
self.bg = mcrfpy.Frame(
|
||||
pos=(x, y), size=(width, height),
|
||||
fill_color=mcrfpy.Color(60, 20, 20),
|
||||
outline=2.0, outline_color=mcrfpy.Color(100, 100, 100)
|
||||
)
|
||||
|
||||
# Fill bar
|
||||
self.fill = mcrfpy.Frame(
|
||||
pos=(2, 2), size=(width - 4, height - 4),
|
||||
fill_color=mcrfpy.Color(200, 50, 50)
|
||||
)
|
||||
self.bg.children.append(self.fill)
|
||||
|
||||
# Label
|
||||
self.label = mcrfpy.Caption(
|
||||
text=f"{max_health}/{max_health}",
|
||||
pos=(width // 2 - 30, height // 2 - 10)
|
||||
)
|
||||
self.bg.children.append(self.label)
|
||||
|
||||
def set_health(self, value):
|
||||
self.health = max(0, min(value, self.max_health))
|
||||
ratio = self.health / self.max_health
|
||||
self.fill.animate("w", (self.width - 4) * ratio, 0.3, mcrfpy.Easing.EASE_OUT)
|
||||
self.label.text = f"{self.health}/{self.max_health}"
|
||||
|
||||
# Color based on health
|
||||
if ratio > 0.5:
|
||||
self.fill.fill_color = mcrfpy.Color(100, 200, 100)
|
||||
elif ratio > 0.25:
|
||||
self.fill.fill_color = mcrfpy.Color(200, 200, 50)
|
||||
else:
|
||||
self.fill.fill_color = mcrfpy.Color(200, 50, 50)
|
||||
|
||||
hp_bar = HealthBar(312, 300, 400, 40, 100)
|
||||
scene.children.append(hp_bar.bg)
|
||||
|
||||
scene.children.append(mcrfpy.Caption(text="Press 1-5 to set health", pos=(380, 400)))
|
||||
|
||||
def on_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED: return
|
||||
if key == mcrfpy.Key.NUM_1: hp_bar.set_health(100)
|
||||
elif key == mcrfpy.Key.NUM_2: hp_bar.set_health(75)
|
||||
elif key == mcrfpy.Key.NUM_3: hp_bar.set_health(50)
|
||||
elif key == mcrfpy.Key.NUM_4: hp_bar.set_health(25)
|
||||
elif key == mcrfpy.Key.NUM_5: hp_bar.set_health(0)
|
||||
|
||||
scene.on_key = on_key
|
||||
52
tests/snippets/074_ui_tooltip.py
Normal file
52
tests/snippets/074_ui_tooltip.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Vector] verified=0.2.8-dev status=ok
|
||||
# UI Tooltip - Hover tooltip display
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Tooltip (hidden by default)
|
||||
tooltip = mcrfpy.Frame(
|
||||
pos=(0, 0), size=(200, 60),
|
||||
fill_color=mcrfpy.Color(40, 40, 50),
|
||||
outline=1.0, outline_color=mcrfpy.Color(150, 150, 150),
|
||||
visible=False
|
||||
)
|
||||
tooltip_text = mcrfpy.Caption(text="", pos=(10, 10))
|
||||
tooltip.children.append(tooltip_text)
|
||||
|
||||
def make_item(x, y, name, description):
|
||||
item = mcrfpy.Frame(
|
||||
pos=(x, y), size=(150, 150),
|
||||
fill_color=mcrfpy.Color(80, 80, 120)
|
||||
)
|
||||
label = mcrfpy.Caption(text=name, pos=(30, 60))
|
||||
item.children.append(label)
|
||||
|
||||
def on_enter(pos):
|
||||
tooltip.visible = True
|
||||
tooltip_text.text = description
|
||||
tooltip.pos = mcrfpy.Vector(pos.x + 20, pos.y + 20)
|
||||
|
||||
def on_exit(pos):
|
||||
tooltip.visible = False
|
||||
|
||||
def on_move(pos):
|
||||
tooltip.pos = mcrfpy.Vector(pos.x + 20, pos.y + 20)
|
||||
|
||||
item.on_enter = on_enter
|
||||
item.on_exit = on_exit
|
||||
item.on_move = on_move
|
||||
return item
|
||||
|
||||
scene.children.append(make_item(200, 300, "Sword", "A sharp blade"))
|
||||
scene.children.append(make_item(400, 300, "Shield", "Blocks attacks"))
|
||||
scene.children.append(make_item(600, 300, "Potion", "Heals 50 HP"))
|
||||
scene.children.append(tooltip) # Add last for z-order
|
||||
|
||||
status = mcrfpy.Caption(text="Hover over items to see tooltips", pos=(350, 550))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
62
tests/snippets/075_ui_message_log.py
Normal file
62
tests/snippets/075_ui_message_log.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# UI Message Log - Scrolling text messages
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
class MessageLog:
|
||||
def __init__(self, x, y, width, height, max_messages=8):
|
||||
self.max_messages = max_messages
|
||||
self.messages = []
|
||||
|
||||
self.container = mcrfpy.Frame(
|
||||
pos=(x, y), size=(width, height),
|
||||
fill_color=mcrfpy.Color(20, 20, 30),
|
||||
outline=2.0, outline_color=mcrfpy.Color(80, 80, 100),
|
||||
clip_children=True
|
||||
)
|
||||
|
||||
def add_message(self, text, color):
|
||||
self.messages.append((text, color))
|
||||
if len(self.messages) > self.max_messages:
|
||||
self.messages.pop(0)
|
||||
self._refresh()
|
||||
|
||||
def _refresh(self):
|
||||
# Clear existing
|
||||
while len(self.container.children) > 0:
|
||||
self.container.children.remove(self.container.children[0])
|
||||
|
||||
# Add messages
|
||||
for i, (text, color) in enumerate(self.messages):
|
||||
cap = mcrfpy.Caption(text=text, pos=(10, 10 + i * 25))
|
||||
cap.fill_color = color
|
||||
self.container.children.append(cap)
|
||||
|
||||
log = MessageLog(262, 200, 500, 250)
|
||||
scene.children.append(log.container)
|
||||
|
||||
messages = [
|
||||
("You enter the dungeon.", mcrfpy.Color(200, 200, 200)),
|
||||
("A goblin appears!", mcrfpy.Color(255, 100, 100)),
|
||||
("You attack the goblin.", mcrfpy.Color(255, 200, 100)),
|
||||
("The goblin is defeated!", mcrfpy.Color(100, 255, 100)),
|
||||
("You found gold!", mcrfpy.Color(255, 255, 100)),
|
||||
("You descend deeper...", mcrfpy.Color(150, 150, 255)),
|
||||
]
|
||||
|
||||
msg_idx = 0
|
||||
def add_msg(timer, runtime):
|
||||
global msg_idx
|
||||
if msg_idx < len(messages):
|
||||
log.add_message(*messages[msg_idx])
|
||||
msg_idx += 1
|
||||
|
||||
timer = mcrfpy.Timer("msg", add_msg, 1000)
|
||||
|
||||
_caption = mcrfpy.Caption(text="Message Log Demo", pos=(420, 100))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
55
tests/snippets/076_effect_screen_shake.py
Normal file
55
tests/snippets/076_effect_screen_shake.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Key,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Effect Screen Shake - Impact feedback
|
||||
import mcrfpy
|
||||
import random
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Container for all game elements
|
||||
game_container = mcrfpy.Frame(
|
||||
pos=(0, 0), size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(30, 30, 40)
|
||||
)
|
||||
scene.children.append(game_container)
|
||||
|
||||
# Some game content
|
||||
for i in range(5):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(150 + i * 150, 300), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(100 + i * 30, 100, 200 - i * 30)
|
||||
)
|
||||
game_container.children.append(frame)
|
||||
|
||||
original_pos = (0, 0)
|
||||
shake_timer = None
|
||||
|
||||
def do_shake(timer, runtime):
|
||||
offset_x = random.randint(-10, 10)
|
||||
offset_y = random.randint(-10, 10)
|
||||
game_container.x = offset_x
|
||||
game_container.y = offset_y
|
||||
|
||||
def end_shake(timer, runtime):
|
||||
global shake_timer
|
||||
if shake_timer:
|
||||
shake_timer.stop()
|
||||
shake_timer = None
|
||||
game_container.x = original_pos[0]
|
||||
game_container.y = original_pos[1]
|
||||
|
||||
def trigger_shake():
|
||||
global shake_timer
|
||||
shake_timer = mcrfpy.Timer("shake", do_shake, 16)
|
||||
mcrfpy.Timer("shake_end", end_shake, 300, once=True)
|
||||
|
||||
game_container.children.append(mcrfpy.Caption(
|
||||
text="Press SPACE for screen shake",
|
||||
pos=(350, 550)
|
||||
))
|
||||
|
||||
def on_key(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
trigger_shake()
|
||||
|
||||
scene.on_key = on_key
|
||||
55
tests/snippets/077_effect_damage_flash.py
Normal file
55
tests/snippets/077_effect_damage_flash.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,InputState,Key,Scene,Sprite,Timer] verified=0.2.8-dev status=ok
|
||||
# Effect Damage Flash - Hit feedback on frame
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Character frame (use Frame since Sprite doesn't support color tinting)
|
||||
character = mcrfpy.Frame(
|
||||
pos=(400, 250), size=(200, 200),
|
||||
fill_color=mcrfpy.Color(100, 150, 255)
|
||||
)
|
||||
scene.children.append(character)
|
||||
|
||||
# Add sprite inside frame
|
||||
sprite = mcrfpy.Sprite(
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=84,
|
||||
pos=(50, 50),
|
||||
scale=6.0
|
||||
)
|
||||
character.children.append(sprite)
|
||||
|
||||
scene.children.append(mcrfpy.Caption(
|
||||
text="Press SPACE to damage",
|
||||
pos=(400, 550)
|
||||
))
|
||||
|
||||
hp_label = mcrfpy.Caption(text="HP: 100", pos=(450, 500))
|
||||
hp_label.fill_color = mcrfpy.Color(100, 255, 100)
|
||||
scene.children.append(hp_label)
|
||||
|
||||
hp = 100
|
||||
|
||||
def flash_damage():
|
||||
global hp
|
||||
hp = max(0, hp - 10)
|
||||
hp_label.text = f"HP: {hp}"
|
||||
|
||||
# Flash white then red then back
|
||||
character.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
character.animate("fill_color", (255, 100, 100, 255), 0.1, mcrfpy.Easing.LINEAR)
|
||||
|
||||
def restore(timer, runtime):
|
||||
character.animate("fill_color", (100, 150, 255, 255), 0.2, mcrfpy.Easing.EASE_OUT)
|
||||
|
||||
mcrfpy.Timer("flash_restore", restore, 150, once=True)
|
||||
|
||||
def on_key(key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
flash_damage()
|
||||
|
||||
scene.on_key = on_key
|
||||
50
tests/snippets/078_effect_floating_text.py
Normal file
50
tests/snippets/078_effect_floating_text.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,InputState,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Effect Floating Text - Damage numbers
|
||||
import mcrfpy
|
||||
import random
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Target
|
||||
target = mcrfpy.Frame(
|
||||
pos=(412, 284), size=(200, 200),
|
||||
fill_color=mcrfpy.Color(100, 80, 80)
|
||||
)
|
||||
scene.children.append(target)
|
||||
|
||||
scene.children.append(mcrfpy.Caption(text="Click target to deal damage", pos=(360, 550)))
|
||||
|
||||
text_id = 0
|
||||
|
||||
def spawn_damage_text(x, y, damage):
|
||||
global text_id
|
||||
text_id += 1
|
||||
text = mcrfpy.Caption(text=f"-{damage}", pos=(x, y))
|
||||
text.fill_color = mcrfpy.Color(255, 100, 100)
|
||||
text.font_size = 32
|
||||
scene.children.append(text)
|
||||
|
||||
# Float up and fade
|
||||
text.animate("y", y - 100, 1.0, mcrfpy.Easing.EASE_OUT)
|
||||
text.animate("opacity", 0.0, 1.0, mcrfpy.Easing.EASE_IN)
|
||||
|
||||
# Remove after animation using closure to capture text reference
|
||||
current_text = text
|
||||
def remove(timer, runtime):
|
||||
try:
|
||||
scene.children.remove(current_text)
|
||||
except:
|
||||
pass
|
||||
|
||||
mcrfpy.Timer(f"remove_{text_id}", remove, 1100, once=True)
|
||||
|
||||
def on_click(pos, button, action):
|
||||
if action == mcrfpy.InputState.PRESSED:
|
||||
damage = random.randint(10, 99)
|
||||
# pos is relative to target, add target position
|
||||
spawn_damage_text(pos.x + 412 + 50, pos.y + 284, damage)
|
||||
|
||||
target.on_click = on_click
|
||||
39
tests/snippets/079_effect_pulse.py
Normal file
39
tests/snippets/079_effect_pulse.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Effect Pulse - Pulsing highlight
|
||||
import mcrfpy
|
||||
import math
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Pulsing frame
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(362, 284), size=(300, 200),
|
||||
fill_color=mcrfpy.Color(100, 100, 200),
|
||||
outline=4.0,
|
||||
outline_color=mcrfpy.Color(150, 150, 255)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
label = mcrfpy.Caption(text="Pulsing Effect", pos=(80, 80))
|
||||
frame.children.append(label)
|
||||
|
||||
time = 0
|
||||
|
||||
def pulse(timer, runtime):
|
||||
global time
|
||||
time += 0.1
|
||||
|
||||
# Oscillate opacity
|
||||
opacity = 0.5 + 0.5 * math.sin(time)
|
||||
frame.opacity = opacity
|
||||
|
||||
# Oscillate outline
|
||||
outline = 2 + 4 * abs(math.sin(time))
|
||||
frame.outline = outline
|
||||
|
||||
timer = mcrfpy.Timer("pulse", pulse, 50)
|
||||
|
||||
scene.children.append(mcrfpy.Caption(text="Continuous pulse animation", pos=(380, 550)))
|
||||
67
tests/snippets/080_game_turn_based.py
Normal file
67
tests/snippets/080_game_turn_based.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,InputState,Key,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Game Turn-Based - Simple turn system
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
grid.at(x, y).walkable = True
|
||||
|
||||
# Player
|
||||
player = mcrfpy.Entity(grid_pos=(3, 6), texture=mcrfpy.default_texture, sprite_index=84)
|
||||
grid.entities.append(player)
|
||||
|
||||
# Enemy
|
||||
enemy = mcrfpy.Entity(grid_pos=(12, 6), texture=mcrfpy.default_texture, sprite_index=112)
|
||||
grid.entities.append(enemy)
|
||||
|
||||
turn = "player"
|
||||
status = mcrfpy.Caption(text="Player turn - WASD to move", pos=(320, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
|
||||
def enemy_turn(timer, runtime):
|
||||
global turn
|
||||
# Simple AI: move toward player
|
||||
dx = 1 if player.grid_x > enemy.grid_x else -1 if player.grid_x < enemy.grid_x else 0
|
||||
dy = 1 if player.grid_y > enemy.grid_y else -1 if player.grid_y < enemy.grid_y else 0
|
||||
new_x = enemy.grid_x + dx
|
||||
new_y = enemy.grid_y + dy
|
||||
if grid.at(new_x, new_y).walkable:
|
||||
enemy.grid_pos = (new_x, new_y)
|
||||
turn = "player"
|
||||
status.text = "Player turn - WASD to move"
|
||||
|
||||
def on_key(key, action):
|
||||
global turn
|
||||
if action != mcrfpy.InputState.PRESSED or turn != "player":
|
||||
return
|
||||
|
||||
x, y = player.grid_x, player.grid_y
|
||||
nx, ny = x, y
|
||||
if key == mcrfpy.Key.W: ny = y - 1
|
||||
elif key == mcrfpy.Key.S: ny = y + 1
|
||||
elif key == mcrfpy.Key.A: nx = x - 1
|
||||
elif key == mcrfpy.Key.D: nx = x + 1
|
||||
else: return
|
||||
|
||||
if 0 <= nx < 16 and 0 <= ny < 12 and grid.at(nx, ny).walkable:
|
||||
player.grid_pos = (nx, ny)
|
||||
turn = "enemy"
|
||||
status.text = "Enemy turn..."
|
||||
mcrfpy.Timer("enemy", enemy_turn, 500, once=True)
|
||||
|
||||
scene.on_key = on_key
|
||||
57
tests/snippets/081_game_inventory.py
Normal file
57
tests/snippets/081_game_inventory.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Sprite] verified=0.2.8-dev status=ok
|
||||
# Game Inventory - Grid-based inventory UI
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Inventory grid
|
||||
inv_frame = mcrfpy.Frame(
|
||||
pos=(262, 184), size=(500, 400),
|
||||
fill_color=mcrfpy.Color(40, 40, 60),
|
||||
outline=3.0, outline_color=mcrfpy.Color(100, 100, 140)
|
||||
)
|
||||
scene.children.append(inv_frame)
|
||||
|
||||
inv_frame.children.append(mcrfpy.Caption(text="Inventory", pos=(200, 20)))
|
||||
|
||||
# Create inventory slots
|
||||
slots = []
|
||||
items = [84, 17, 65, 0, 0, 0, 112, 0, 88, 0, 0, 0] # Sprite indices, 0 = empty
|
||||
|
||||
for i in range(12):
|
||||
x = 30 + (i % 4) * 115
|
||||
y = 70 + (i // 4) * 100
|
||||
|
||||
slot = mcrfpy.Frame(
|
||||
pos=(x, y), size=(90, 80),
|
||||
fill_color=mcrfpy.Color(60, 60, 80),
|
||||
outline=2.0, outline_color=mcrfpy.Color(80, 80, 100)
|
||||
)
|
||||
inv_frame.children.append(slot)
|
||||
|
||||
if items[i] > 0:
|
||||
sprite = mcrfpy.Sprite(
|
||||
texture=mcrfpy.default_texture,
|
||||
sprite_index=items[i],
|
||||
pos=(25, 15),
|
||||
scale=3.0
|
||||
)
|
||||
slot.children.append(sprite)
|
||||
|
||||
# Hover effect
|
||||
def make_hover(s):
|
||||
def enter(pos): s.fill_color = mcrfpy.Color(80, 80, 100)
|
||||
def exit(pos): s.fill_color = mcrfpy.Color(60, 60, 80)
|
||||
return enter, exit
|
||||
|
||||
enter, exit = make_hover(slot)
|
||||
slot.on_enter = enter
|
||||
slot.on_exit = exit
|
||||
|
||||
status = mcrfpy.Caption(text="Hover over slots to highlight", pos=(400, 620))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
66
tests/snippets/082_game_minimap.py
Normal file
66
tests/snippets/082_game_minimap.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,InputState,Key,Scene] verified=0.2.8-dev status=ok
|
||||
# Game Minimap - Small overview map
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
# Main game grid - fills most of the screen
|
||||
main_grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(800, 600),
|
||||
zoom=3.0
|
||||
)
|
||||
scene.children.append(main_grid)
|
||||
|
||||
# Create dungeon
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = main_grid.at(x, y)
|
||||
if x == 0 or x == 15 or y == 0 or y == 11:
|
||||
cell.tilesprite = 1
|
||||
elif x == 8 and y > 2 and y < 9:
|
||||
cell.tilesprite = 1
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
|
||||
player = mcrfpy.Entity(grid_pos=(4, 6), texture=mcrfpy.default_texture, sprite_index=84)
|
||||
main_grid.entities.append(player)
|
||||
|
||||
# Minimap - small overview in corner
|
||||
minimap = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(820, 20),
|
||||
size=(180, 135),
|
||||
zoom=0.7
|
||||
)
|
||||
scene.children.append(minimap)
|
||||
|
||||
# Copy layout to minimap
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
minimap.at(x, y).tilesprite = main_grid.at(x, y).tilesprite
|
||||
|
||||
# Minimap player marker
|
||||
mini_player = mcrfpy.Entity(grid_pos=(4, 6), texture=mcrfpy.default_texture, sprite_index=84)
|
||||
minimap.entities.append(mini_player)
|
||||
|
||||
def on_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED: return
|
||||
x, y = player.grid_x, player.grid_y
|
||||
if key == mcrfpy.Key.W and main_grid.at(x, y-1).tilesprite == 48: y -= 1
|
||||
elif key == mcrfpy.Key.S and main_grid.at(x, y+1).tilesprite == 48: y += 1
|
||||
elif key == mcrfpy.Key.A and main_grid.at(x-1, y).tilesprite == 48: x -= 1
|
||||
elif key == mcrfpy.Key.D and main_grid.at(x+1, y).tilesprite == 48: x += 1
|
||||
player.grid_pos = (x, y)
|
||||
mini_player.grid_pos = (x, y)
|
||||
|
||||
scene.on_key = on_key
|
||||
|
||||
status = mcrfpy.Caption(text="WASD to move, watch minimap", pos=(320, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
43
tests/snippets/083_grid_layers.py
Normal file
43
tests/snippets/083_grid_layers.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Grid,Scene,TileLayer] verified=0.2.8-dev status=ok
|
||||
# Grid Layers - Multiple tile layers
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Base layer - floor
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
# Add a color overlay layer
|
||||
color_layer = mcrfpy.ColorLayer(z_index=1, name="highlights")
|
||||
grid.add_layer(color_layer)
|
||||
|
||||
# Highlight some cells with color
|
||||
for x in range(4, 12):
|
||||
color_layer.set((x, 5), mcrfpy.Color(100, 200, 255, 100))
|
||||
color_layer.set((x, 6), mcrfpy.Color(100, 200, 255, 100))
|
||||
|
||||
# Add a tile layer for decorations
|
||||
tile_layer = mcrfpy.TileLayer(z_index=2, name="decorations", texture=mcrfpy.default_texture)
|
||||
grid.add_layer(tile_layer)
|
||||
|
||||
# Add some decoration tiles
|
||||
tile_layer.set((5, 3), 17) # Chest
|
||||
tile_layer.set((10, 3), 65) # Door
|
||||
tile_layer.set((7, 8), 80) # Water
|
||||
|
||||
status = mcrfpy.Caption(text="Grid with multiple layers: base + color + decoration", pos=(280, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
46
tests/snippets/084_bresenham_line.py
Normal file
46
tests/snippets/084_bresenham_line.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Bresenham Line - Draw lines on grid
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(20, 15),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(112, 84),
|
||||
size=(800, 600)
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add color layer for line visualization
|
||||
line_layer = mcrfpy.ColorLayer(z_index=1, name="line")
|
||||
grid.add_layer(line_layer)
|
||||
|
||||
# Fill with floor
|
||||
for y in range(15):
|
||||
for x in range(20):
|
||||
grid.at(x, y).tilesprite = 48
|
||||
|
||||
# Draw lines using bresenham
|
||||
start = (2, 2)
|
||||
end = (17, 12)
|
||||
|
||||
# Get all cells along the line
|
||||
line_cells = mcrfpy.bresenham(start, end)
|
||||
|
||||
# Highlight the line
|
||||
for x, y in line_cells:
|
||||
line_layer.set((x, y), mcrfpy.Color(255, 200, 100, 200))
|
||||
|
||||
# Mark start and end
|
||||
line_layer.set((start[0], start[1]), mcrfpy.Color(100, 255, 100, 255))
|
||||
line_layer.set((end[0], end[1]), mcrfpy.Color(255, 100, 100, 255))
|
||||
|
||||
status = mcrfpy.Caption(
|
||||
text=f"Bresenham line: {len(line_cells)} cells - Green=start, Red=end",
|
||||
pos=(280, 700)
|
||||
)
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
50
tests/snippets/085_entity_visibility.py
Normal file
50
tests/snippets/085_entity_visibility.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# mcrf: objects=[Caption,Color,Entity,Grid,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Entity Visibility - Check what entities can see
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Create dungeon
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
if x == 8 and y > 1 and y < 10:
|
||||
cell.tilesprite = 1
|
||||
cell.transparent = False
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
cell.transparent = True
|
||||
|
||||
# Player
|
||||
player = mcrfpy.Entity(grid_pos=(4, 6), texture=mcrfpy.default_texture, sprite_index=84, name="player")
|
||||
grid.entities.append(player)
|
||||
|
||||
# Enemies
|
||||
for i, pos in enumerate([(12, 3), (12, 6), (12, 9)]):
|
||||
enemy = mcrfpy.Entity(grid_pos=pos, texture=mcrfpy.default_texture, sprite_index=112, name=f"enemy_{i}")
|
||||
grid.entities.append(enemy)
|
||||
|
||||
grid.perspective = player
|
||||
grid.fov_radius = 10
|
||||
|
||||
status = mcrfpy.Caption(text="Wall blocks view of some enemies", pos=(320, 700))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
|
||||
def update_status(timer, runtime):
|
||||
visible = player.visible_entities()
|
||||
names = [e.name for e in visible if e.name != "player"]
|
||||
status.text = f"Visible entities: {names if names else 'none'}"
|
||||
|
||||
timer = mcrfpy.Timer("check", update_status, 100)
|
||||
59
tests/snippets/086_procgen_cellular.py
Normal file
59
tests/snippets/086_procgen_cellular.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# mcrf: objects=[Caption,Grid,Scene] verified=0.2.8-dev status=ok
|
||||
# Procgen Cellular - Cave generation
|
||||
import mcrfpy
|
||||
import random
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(40, 30),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(112, 84),
|
||||
size=(800, 600)
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Initialize with random walls
|
||||
for y in range(30):
|
||||
for x in range(40):
|
||||
cell = grid.at(x, y)
|
||||
if x == 0 or x == 39 or y == 0 or y == 29:
|
||||
cell.tilesprite = 1
|
||||
elif random.random() < 0.45:
|
||||
cell.tilesprite = 1
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
|
||||
# Cellular automata iterations
|
||||
def count_neighbors(x, y):
|
||||
count = 0
|
||||
for dy in range(-1, 2):
|
||||
for dx in range(-1, 2):
|
||||
if dx == 0 and dy == 0:
|
||||
continue
|
||||
nx, ny = x + dx, y + dy
|
||||
if 0 <= nx < 40 and 0 <= ny < 30:
|
||||
if grid.at(nx, ny).tilesprite == 1:
|
||||
count += 1
|
||||
else:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def iterate():
|
||||
changes = []
|
||||
for y in range(1, 29):
|
||||
for x in range(1, 39):
|
||||
neighbors = count_neighbors(x, y)
|
||||
if neighbors > 4:
|
||||
changes.append((x, y, 1))
|
||||
elif neighbors < 4:
|
||||
changes.append((x, y, 48))
|
||||
for x, y, tile in changes:
|
||||
grid.at(x, y).tilesprite = tile
|
||||
|
||||
# Run 4 iterations
|
||||
for _ in range(4):
|
||||
iterate()
|
||||
|
||||
scene.children.append(mcrfpy.Caption(text="Cellular automata cave generation", pos=(340, 700)))
|
||||
54
tests/snippets/087_grid_point_state.py
Normal file
54
tests/snippets/087_grid_point_state.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# mcrf: objects=[Caption,Color,ColorLayer,Grid,InputState,Scene] verified=0.2.8-dev status=ok
|
||||
# Grid Point State - Cell properties
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(16, 12),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=4.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Add visual layer for highlighting
|
||||
highlight_layer = mcrfpy.ColorLayer(z_index=1, name="highlight")
|
||||
grid.add_layer(highlight_layer)
|
||||
|
||||
# Setup cells with different properties
|
||||
for y in range(12):
|
||||
for x in range(16):
|
||||
cell = grid.at(x, y)
|
||||
cell.tilesprite = 48
|
||||
cell.walkable = True
|
||||
cell.transparent = True
|
||||
|
||||
# Mark some cells as walls
|
||||
for i in range(5):
|
||||
cell = grid.at(8, 3 + i)
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
cell.transparent = False
|
||||
|
||||
# Highlight left half as "explored"
|
||||
for x in range(8):
|
||||
for y in range(12):
|
||||
highlight_layer.set((x, y), mcrfpy.Color(100, 200, 100, 80))
|
||||
|
||||
# Show cell info on click
|
||||
status = mcrfpy.Caption(text="Click a cell to see properties", pos=(350, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
|
||||
def on_cell_click(pos, button, action):
|
||||
if action == mcrfpy.InputState.PRESSED:
|
||||
x, y = int(pos.x), int(pos.y)
|
||||
if 0 <= x < 16 and 0 <= y < 12:
|
||||
cell = grid.at(x, y)
|
||||
status.text = f"Cell ({x},{y}): walkable={cell.walkable}, transparent={cell.transparent}"
|
||||
|
||||
grid.on_cell_click = on_cell_click
|
||||
49
tests/snippets/088_animation_multiple.py
Normal file
49
tests/snippets/088_animation_multiple.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Animation Multiple - Several animations at once
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Create a frame
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100, 300), size=(100, 100),
|
||||
fill_color=mcrfpy.Color(255, 100, 100),
|
||||
outline=2.0,
|
||||
outline_color=mcrfpy.Color(255, 255, 255),
|
||||
opacity=1.0
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
scene.children.append(mcrfpy.Caption(
|
||||
text="Multiple animations running simultaneously",
|
||||
pos=(300, 600)
|
||||
))
|
||||
|
||||
def restart_animation(timer, runtime):
|
||||
frame.x = 100
|
||||
frame.y = 300
|
||||
frame.w = 100
|
||||
frame.h = 100
|
||||
frame.fill_color = mcrfpy.Color(255, 100, 100)
|
||||
frame.opacity = 1.0
|
||||
frame.outline = 2.0
|
||||
frame.animate("x", 800, 3.0, mcrfpy.Easing.EASE_IN_OUT)
|
||||
frame.animate("y", 500, 3.0, mcrfpy.Easing.EASE_IN_OUT_BACK)
|
||||
frame.animate("w", 200, 3.0, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
frame.animate("h", 150, 3.0, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
frame.animate("fill_color", (100, 100, 255, 255), 3.0, mcrfpy.Easing.LINEAR)
|
||||
frame.animate("opacity", 0.5, 3.0, mcrfpy.Easing.EASE_IN)
|
||||
frame.animate("outline", 10.0, 3.0, mcrfpy.Easing.LINEAR)
|
||||
|
||||
# Start multiple animations at the same time
|
||||
frame.animate("x", 800, 3.0, mcrfpy.Easing.EASE_IN_OUT)
|
||||
frame.animate("y", 500, 3.0, mcrfpy.Easing.EASE_IN_OUT_BACK)
|
||||
frame.animate("w", 200, 3.0, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
frame.animate("h", 150, 3.0, mcrfpy.Easing.EASE_OUT_ELASTIC)
|
||||
frame.animate("fill_color", (100, 100, 255, 255), 3.0, mcrfpy.Easing.LINEAR)
|
||||
frame.animate("opacity", 0.5, 3.0, mcrfpy.Easing.EASE_IN)
|
||||
frame.animate("outline", 10.0, 3.0, mcrfpy.Easing.LINEAR)
|
||||
loop_timer = mcrfpy.Timer("loop", restart_animation, 8000)
|
||||
60
tests/snippets/089_camera_follow.py
Normal file
60
tests/snippets/089_camera_follow.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# mcrf: objects=[Caption,Color,Easing,Entity,Grid,InputState,Key,Scene] verified=0.2.8-dev status=ok
|
||||
# Camera Follow - Camera tracks entity
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(30, 30),
|
||||
texture=mcrfpy.default_texture,
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
zoom=2.0
|
||||
)
|
||||
scene.children.append(grid)
|
||||
|
||||
# Create large map
|
||||
for y in range(30):
|
||||
for x in range(30):
|
||||
cell = grid.at(x, y)
|
||||
if x == 0 or x == 29 or y == 0 or y == 29:
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
elif (x + y) % 7 == 0 and x > 1 and x < 28 and y > 1 and y < 28:
|
||||
cell.tilesprite = 1
|
||||
cell.walkable = False
|
||||
else:
|
||||
cell.tilesprite = 48
|
||||
cell.walkable = True
|
||||
|
||||
# Player in center
|
||||
player = mcrfpy.Entity(grid_pos=(15, 15), texture=mcrfpy.default_texture, sprite_index=84)
|
||||
grid.entities.append(player)
|
||||
|
||||
# Center camera on player
|
||||
grid.center_camera((15, 15))
|
||||
|
||||
def on_key(key, action):
|
||||
if action != mcrfpy.InputState.PRESSED:
|
||||
return
|
||||
x, y = player.grid_x, player.grid_y
|
||||
nx, ny = x, y
|
||||
if key == mcrfpy.Key.W: ny -= 1
|
||||
elif key == mcrfpy.Key.S: ny += 1
|
||||
elif key == mcrfpy.Key.A: nx -= 1
|
||||
elif key == mcrfpy.Key.D: nx += 1
|
||||
else: return
|
||||
|
||||
if grid.at(nx, ny).walkable:
|
||||
player.grid_pos = (nx, ny)
|
||||
# Animate camera to follow (center uses pixel coords: tile * 16 for 16x16 sprites)
|
||||
grid.animate("center_x", nx * 16 + 8, 0.2, mcrfpy.Easing.EASE_OUT)
|
||||
grid.animate("center_y", ny * 16 + 8, 0.2, mcrfpy.Easing.EASE_OUT)
|
||||
|
||||
scene.on_key = on_key
|
||||
|
||||
status = mcrfpy.Caption(text="WASD - Camera follows player", pos=(380, 720))
|
||||
status.outline = 2
|
||||
status.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(status)
|
||||
45
tests/snippets/090_scene_lifecycle.py
Normal file
45
tests/snippets/090_scene_lifecycle.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,InputState,Key,Scene] verified=0.2.8-dev status=ok
|
||||
# Scene Lifecycle - on_enter/on_exit callbacks
|
||||
import mcrfpy
|
||||
|
||||
# Create scenes with lifecycle callbacks
|
||||
class MenuScene(mcrfpy.Scene):
|
||||
def __init__(self):
|
||||
super().__init__("menu")
|
||||
self.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(50, 50, 80)))
|
||||
self.status = mcrfpy.Caption(text="Menu Scene", pos=(420, 300))
|
||||
self.status.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
self.children.append(self.status)
|
||||
self.children.append(mcrfpy.Caption(text="Press SPACE for game", pos=(380, 400)))
|
||||
self.on_key = self.handle_key
|
||||
|
||||
def on_enter(self):
|
||||
self.status.text = "Menu Scene (entered!)"
|
||||
|
||||
def on_exit(self):
|
||||
print("Leaving menu scene")
|
||||
|
||||
def handle_key(self, key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
game_scene.activate()
|
||||
|
||||
class GameScene(mcrfpy.Scene):
|
||||
def __init__(self):
|
||||
super().__init__("game")
|
||||
self.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(80, 50, 50)))
|
||||
self.status = mcrfpy.Caption(text="Game Scene", pos=(420, 300))
|
||||
self.status.fill_color = mcrfpy.Color(100, 255, 100)
|
||||
self.children.append(self.status)
|
||||
self.children.append(mcrfpy.Caption(text="Press SPACE for menu", pos=(380, 400)))
|
||||
self.on_key = self.handle_key
|
||||
|
||||
def on_enter(self):
|
||||
self.status.text = "Game Scene (entered!)"
|
||||
|
||||
def handle_key(self, key, action):
|
||||
if key == mcrfpy.Key.SPACE and action == mcrfpy.InputState.PRESSED:
|
||||
menu_scene.activate()
|
||||
|
||||
menu_scene = MenuScene()
|
||||
game_scene = GameScene()
|
||||
mcrfpy.current_scene = menu_scene
|
||||
44
tests/snippets/091_metrics_display.py
Normal file
44
tests/snippets/091_metrics_display.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene,Timer] verified=0.2.8-dev status=ok
|
||||
# Metrics Display - Performance monitoring
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text="Performance Metrics",
|
||||
pos=(400, 80))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
|
||||
# Create metric displays
|
||||
labels = {}
|
||||
metrics_list = ["frame_time", "fps", "draw_calls", "ui_elements", "visible_elements"]
|
||||
|
||||
for i, name in enumerate(metrics_list):
|
||||
label = mcrfpy.Caption(text=f"{name}: --", pos=(300, 180 + i * 60))
|
||||
scene.children.append(label)
|
||||
labels[name] = label
|
||||
|
||||
# Add some elements to track
|
||||
for i in range(50):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(100 + (i % 10) * 80, 500 + (i // 10) * 50),
|
||||
size=(60, 40),
|
||||
fill_color=mcrfpy.Color(80 + i * 3, 80, 150 - i * 2)
|
||||
)
|
||||
scene.children.append(frame)
|
||||
|
||||
def update_metrics(timer, runtime):
|
||||
metrics = mcrfpy.getMetrics()
|
||||
for name in metrics_list:
|
||||
if name in metrics:
|
||||
value = metrics[name]
|
||||
if isinstance(value, float):
|
||||
labels[name].text = f"{name}: {value:.2f}"
|
||||
else:
|
||||
labels[name].text = f"{name}: {value}"
|
||||
|
||||
timer = mcrfpy.Timer("metrics", update_metrics, 100)
|
||||
48
tests/snippets/092_global_pos.py
Normal file
48
tests/snippets/092_global_pos.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# mcrf: objects=[Caption,Color,Frame,Scene] verified=0.2.8-dev status=ok
|
||||
# Global Position - World coordinates
|
||||
import mcrfpy
|
||||
|
||||
scene = mcrfpy.Scene("demo")
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
scene.children.append(mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 40)))
|
||||
|
||||
# Nested containers
|
||||
outer = mcrfpy.Frame(
|
||||
pos=(200, 200), size=(500, 400),
|
||||
fill_color=mcrfpy.Color(60, 60, 80),
|
||||
outline=2.0
|
||||
)
|
||||
scene.children.append(outer)
|
||||
|
||||
inner = mcrfpy.Frame(
|
||||
pos=(100, 100), size=(300, 200),
|
||||
fill_color=mcrfpy.Color(80, 80, 100),
|
||||
outline=2.0
|
||||
)
|
||||
outer.children.append(inner)
|
||||
|
||||
deepest = mcrfpy.Frame(
|
||||
pos=(50, 50), size=(100, 80),
|
||||
fill_color=mcrfpy.Color(150, 100, 100),
|
||||
outline=2.0
|
||||
)
|
||||
inner.children.append(deepest)
|
||||
|
||||
# Show positions
|
||||
outer.children.append(mcrfpy.Caption(text="Outer: pos=(200,200)", pos=(10, 10)))
|
||||
inner.children.append(mcrfpy.Caption(text="Inner: pos=(100,100)", pos=(10, 10)))
|
||||
|
||||
# Global position
|
||||
gpos = deepest.global_bounds
|
||||
deepest.children.append(mcrfpy.Caption(
|
||||
text=f"local=(50,50)",
|
||||
pos=(5, 5)
|
||||
))
|
||||
|
||||
_caption = mcrfpy.Caption(
|
||||
text=f"Deepest global_pos = ({gpos[0].x}, {gpos[0].y})",
|
||||
pos=(300, 650))
|
||||
_caption.fill_color = mcrfpy.Color(255, 220, 100)
|
||||
scene.children.append(_caption)
|
||||
scene.children.append(mcrfpy.Caption(text="(200 + 100 + 50 = 350, 200 + 100 + 50 = 350)", pos=(300, 690)))
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue