"""Boîte de collision d'un objet de scène (voir screens/rendering/collision_settings.py, static/js/play/conditions.js:: elementsOverlap pour son usage au jeu, testé côté client dans static/js/play/__tests__/conditions.test.js) : posée AUTOMATIQUEMENT sur CHAQUE objet (= sa propre boîte, tant que rien n'est réglé), activable/ désactivable, taille/forme/décalage configurables.""" import re import screens def _create_jeu2d_game(client, tmp_game_slug_cleanup, name="pytest_collision"): resp = client.post("/games/new", data={"name": name}, follow_redirects=False) slug = tmp_game_slug_cleanup(resp.headers["Location"].rstrip("/").split("/")[-1]) screen_id = screens.create_screen(slug, "Scène 1", kind="jeu_2d") return slug, screen_id def _add_decor(client, slug, screen_id): resp = client.post(f"/game/{slug}/screens/{screen_id}/scene-objects/add", data={"kind": "decor"}, follow_redirects=False) assert resp.status_code == 302, resp.data return int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1)) def test_freshly_placed_object_defaults_to_its_own_box_as_collision(client, tmp_game_slug_cleanup): """Sans réglage explicite, la boîte de collision EST la boîte de l'objet (comportement inchangé pour tout objet déjà posé — la collision se comportait déjà comme ça, implicitement, avant l'ajout de ce réglage).""" slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) obj_id = _add_decor(client, slug, screen_id) obj = screens.get_scene_object(slug, obj_id) collision = screens.resolve_collision_settings(obj) assert collision["enabled"] is True assert collision["shape"] == "rectangle" assert collision["width"] == obj["width"] assert collision["height"] == obj["height"] assert collision["offset_x"] == 0 and collision["offset_y"] == 0 def test_collision_route_persists_a_custom_box(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) obj_id = _add_decor(client, slug, screen_id) resp = client.post( f"/game/{slug}/scene-objects/{obj_id}/collision", data='{"enabled": true, "shape": "cercle", "width": 30, "height": 30, "offset_x": 5, "offset_y": -5}', content_type="application/json", ) assert resp.status_code == 200 assert resp.get_json()["collision"] == { "enabled": True, "shape": "cercle", "width": 30.0, "height": 30.0, "offset_x": 5.0, "offset_y": -5.0, } obj = screens.get_scene_object(slug, obj_id) assert screens.resolve_collision_settings(obj)["shape"] == "cercle" def test_collision_route_can_disable_the_box_entirely(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) obj_id = _add_decor(client, slug, screen_id) resp = client.post( f"/game/{slug}/scene-objects/{obj_id}/collision", data='{"enabled": false}', content_type="application/json", ) assert resp.get_json()["collision"]["enabled"] is False def test_collision_route_rejects_an_unknown_shape_and_falls_back_to_rectangle(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) obj_id = _add_decor(client, slug, screen_id) resp = client.post( f"/game/{slug}/scene-objects/{obj_id}/collision", data='{"shape": "triangle"}', content_type="application/json", ) assert resp.get_json()["collision"]["shape"] == "rectangle" def test_collision_route_ignores_invalid_size_values(client, tmp_game_slug_cleanup): """Une largeur/hauteur invalide (texte, vide) ne plante pas — retombe sur la taille de l'objet, comme si rien n'avait été réglé.""" slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) obj_id = _add_decor(client, slug, screen_id) resp = client.post( f"/game/{slug}/scene-objects/{obj_id}/collision", data='{"width": "pas-un-nombre", "height": ""}', content_type="application/json", ) obj = screens.get_scene_object(slug, obj_id) collision = resp.get_json()["collision"] assert collision["width"] == obj["width"] assert collision["height"] == obj["height"] def test_collision_route_404s_for_missing_object(client, game): resp = client.post(f"/game/{game}/scene-objects/999999/collision", data="{}", content_type="application/json") assert resp.status_code == 404 def test_full_game_payload_exposes_collision_settings_for_every_scene_object_kind(client, tmp_game_slug_cleanup): """Pas seulement "personnage" — un décor/fond peut aussi servir d'obstacle à une condition de collision.""" slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) obj_id = _add_decor(client, slug, screen_id) payload = screens.full_game_payload(slug) scene_screen = next(s for s in payload["screens"] if s["id"] == screen_id) obj_payload = next(o for o in scene_screen["elements"] if o["id"] == obj_id) assert obj_payload["collision"]["enabled"] is True