Jmx0OyFET0NUWVBFIGh0bWwmZ3Q7CiZsdDtodG1sIGxhbmc9JnF1b3Q7ZW4mcXVvdDsmZ3Q7CiZsdDtoZWFkJmd0OwogICAgJmx0O21ldGEgY2hhcnNldD0mcXVvdDtVVEYtOCZxdW90OyZndDsKICAgICZsdDttZXRhIG5hbWU9JnF1b3Q7dmlld3BvcnQmcXVvdDsgY29udGVudD0mcXVvdDt3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wLCB1c2VyLXNjYWxhYmxlPW5vJnF1b3Q7Jmd0OwogICAgJmx0O3RpdGxlJmd0O0TogIHluIjnmoTmjqXmmJ/mmJ/lsI/muLjmiI8g4pyoJmx0Oy90aXRsZSZndDsKICAgICZsdDtzdHlsZSZndDsKICAgICAgICBib2R5IHsKICAgICAgICAgICAgYmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KDE0NWRlZywgIzJiMWIzYSwgIzFhMTEyNSk7CiAgICAgICAgICAgIG1pbi1oZWlnaHQ6IDEwMHZoOwogICAgICAgICAgICBkaXNwbGF5OiBmbGV4OwogICAgICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjsKICAgICAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgICAgICAgZm9udC1mYW1pbHk6ICdTZWdvZSBVSScsIFJvYm90bywgc3lzdGVtLXVpLCBzYW5zLXNlcmlmOwogICAgICAgICAgICBtYXJnaW46IDA7CiAgICAgICAgICAgIHBhZGRpbmc6IDEwcHg7CiAgICAgICAgfQogICAgICAgIC5nYW1lLWNvbnRhaW5lciB7CiAgICAgICAgICAgIGJhY2tncm91bmQ6ICMzYTJhNGE7CiAgICAgICAgICAgIHBhZGRpbmc6IDI1cHggMjBweCAyMHB4IDIwcHg7CiAgICAgICAgICAgIGJvcmRlci1yYWRpdXM6IDQ4cHg7CiAgICAgICAgICAgIGJveC1zaGFkb3c6IDAgMjBweCAzMHB4IHJnYmEoMCwwLDAsMC42KSwgaW5zZXQgMCAycHggNHB4IHJnYmEoMjU1LDI1NSwyNTUsMC4xKTsKICAgICAgICAgICAgYm9yZGVyOiAxcHggc29saWQgIzk1NzhiMDsKICAgICAgICB9CiAgICAgICAgaDIgewogICAgICAgICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICAgICAgICAgIG1hcmdpbjogMCAwIDEycHggMDsKICAgICAgICAgICAgZm9udC1zaXplOiAzMnB4OwogICAgICAgICAgICBmb250LXdlaWdodDogNjAwOwogICAgICAgICAgICBjb2xvcjogI2ZmZGU5ZTsKICAgICAgICAgICAgdGV4dC1zaGFkb3c6IDAgNHB4IDAgIzhiNWY5YywgMCA2cHggMTBweCBibGFjazsKICAgICAgICAgICAgbGV0dGVyLXNwYWNpbmc6IDJweDsKICAgICAgICAgICAgd29yZC1icmVhazoga2VlcC1hbGw7CiAgICAgICAgfQogICAgICAgIC5zdGF0cyB7CiAgICAgICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICAgICAgICAgICAgYmFja2dyb3VuZDogIzFlMTMyYjsKICAgICAgICAgICAgcGFkZGluZzogMTJweCAyMHB4OwogICAgICAgICAgICBib3JkZXItcmFkaXVzOiA2MHB4OwogICAgICAgICAgICBtYXJnaW4tYm90dG9tOiAxNXB4OwogICAgICAgICAgICBjb2xvcjogI2ZhZGY5ZTsKICAgICAgICAgICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICAgICAgICAgIGZvbnQtc2l6ZTogMjBweDsKICAgICAgICAgICAgYm9yZGVyOiAycHggc29saWQgI2I0OTRkMDsKICAgICAgICAgICAgYm94LXNoYWRvdzogaW5zZXQgMCAycHggNXB4ICMwYjA4MTA7CiAgICAgICAgfQogICAgICAgIC5zdGF0cyBzcGFuIHsKICAgICAgICAgICAgYmFja2dyb3VuZDogIzJkMWUzYTsKICAgICAgICAgICAgcGFkZGluZzogNXB4IDIwcHg7CiAgICAgICAgICAgIGJvcmRlci1yYWRpdXM6IDQwcHg7CiAgICAgICAgICAgIGNvbG9yOiAjZmZkOTY2OwogICAgICAgICAgICBmb250LXNpemU6IDI0cHg7CiAgICAgICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNjOWE5ZjA7CiAgICAgICAgfQogICAgICAgIGNhbnZhcyB7CiAgICAgICAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgICAgICAgICBtYXJnaW46IDAgYXV0bzsKICAgICAgICAgICAgYm9yZGVyLXJhZGl1czogMjhweDsKICAgICAgICAgICAgYmFja2dyb3VuZDogIzFlMTUyOTsKICAgICAgICAgICAgYm94LXNoYWRvdzogMCAwIDAgM3B4ICNiMjhhZDAsIDAgMTVweCAyMHB4IGJsYWNrOwogICAgICAgICAgICBjdXJzb3I6IG5vbmU7ICAvKiDpmpDol4/pu5jorqTpvKDmoIfvvIzmm7Tlg4/muLjmiI8gKi8KICAgICAgICAgICAgdG91Y2gtYWN0aW9uOiBub25lOyAvKiDpmLLmraLop6bmkbjml7bmu5rliqgv57yp5pS+ICovCiAgICAgICAgICAgIHdpZHRoOiAxMDAlOwogICAgICAgICAgICBtYXgtd2lkdGg6IDQwMHB4OwogICAgICAgICAgICBoZWlnaHQ6IGF1dG87CiAgICAgICAgfQogICAgICAgIC5idXR0b24tYXJlYSB7CiAgICAgICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgICAgIGp1c3RpZnktY29udGVudDogY2VudGVyOwogICAgICAgICAgICBtYXJnaW4tdG9wOiAyMHB4OwogICAgICAgIH0KICAgICAgICBidXR0b24gewogICAgICAgICAgICBiYWNrZ3JvdW5kOiAjZTRiMWZlOwogICAgICAgICAgICBib3JkZXI6IG5vbmU7CiAgICAgICAgICAgIGZvbnQtc2l6ZTogMjhweDsKICAgICAgICAgICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICAgICAgICAgIHBhZGRpbmc6IDEycHggNDBweDsKICAgICAgICAgICAgYm9yZGVyLXJhZGl1czogNTBweDsKICAgICAgICAgICAgY29sb3I6ICMyZjE3NGI7CiAgICAgICAgICAgIGJveC1zaGFkb3c6IDAgOXB4IDAgIzZiM2Y4YiwgMCAxMHB4IDIwcHggYmxhY2s7CiAgICAgICAgICAgIHRyYW5zaXRpb246IDAuMDdzIGVhc2U7CiAgICAgICAgICAgIGJvcmRlcjogMnB4IHNvbGlkICNmZmRjYWE7CiAgICAgICAgICAgIGN1cnNvcjogcG9pbnRlcjsKICAgICAgICB9CiAgICAgICAgYnV0dG9uOmFjdGl2ZSB7CiAgICAgICAgICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlWSg3cHgpOwogICAgICAgICAgICBib3gtc2hhZG93OiAwIDJweCAwICM2YjNmOGIsIDAgOHB4IDEycHggYmxhY2s7CiAgICAgICAgfQogICAgICAgIC5oaW50IHsKICAgICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICAgICAgICBjb2xvcjogI2NkYjVlOTsKICAgICAgICAgICAgbWFyZ2luLXRvcDogMTJweDsKICAgICAgICAgICAgZm9udC1zaXplOiAxNnB4OwogICAgICAgICAgICBmb250LXdlaWdodDogNTAwOwogICAgICAgICAgICB0ZXh0LXNoYWRvdzogMCAycHggM3B4IGJsYWNrOwogICAgICAgIH0KICAgICAgICAuaGludCBpIHsKICAgICAgICAgICAgZm9udC1zdHlsZTogbm9ybWFsOwogICAgICAgICAgICBiYWNrZ3JvdW5kOiAjMmYxZDQxOwogICAgICAgICAgICBwYWRkaW5nOiA0cHggMTBweDsKICAgICAgICAgICAgYm9yZGVyLXJhZGl1czogOTlweDsKICAgICAgICB9CiAgICAgICAgZm9vdGVyIHsKICAgICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICAgICAgICBjb2xvcjogIzdiNjI5MzsKICAgICAgICAgICAgbWFyZ2luLXRvcDogNXB4OwogICAgICAgICAgICBmb250LXNpemU6IDE0cHg7CiAgICAgICAgfQogICAgJmx0Oy9zdHlsZSZndDsKJmx0Oy9oZWFkJmd0OwombHQ7Ym9keSZndDsKJmx0O2RpdiBjbGFzcz0mcXVvdDtnYW1lLWNvbnRhaW5lciZxdW90OyZndDsKICAgICZsdDtoMiZndDvirZAgROiAgeW4iCAmbWlkZG90OyDmmJ/mmJ/mjqXmjqXkuZAg4q2QJmx0Oy9oMiZndDsKICAgICZsdDtkaXYgY2xhc3M9JnF1b3Q7c3RhdHMmcXVvdDsmZ3Q7CiAgICAgICAgJmx0O2RpdiZndDvinKgg5b6X5YiGJmx0Oy9kaXYmZ3Q7CiAgICAgICAgJmx0O3NwYW4gaWQ9JnF1b3Q7c2NvcmVEaXNwbGF5JnF1b3Q7Jmd0OzAmbHQ7L3NwYW4mZ3Q7CiAgICAgICAgJmx0O2RpdiZndDvinaTvuI8g55Sf5ZG9Jmx0Oy9kaXYmZ3Q7CiAgICAgICAgJmx0O3NwYW4gaWQ9JnF1b3Q7bWlzc0Rpc3BsYXkmcXVvdDsmZ3Q7MyZsdDsvc3BhbiZndDsKICAgICZsdDsvZGl2Jmd0OwogICAgJmx0O2NhbnZhcyBpZD0mcXVvdDtnYW1lQ2FudmFzJnF1b3Q7IHdpZHRoPSZxdW90OzQwMCZxdW90OyBoZWlnaHQ9JnF1b3Q7NDAwJnF1b3Q7Jmd0OyZsdDsvY2FudmFzJmd0OwogICAgJmx0O2RpdiBjbGFzcz0mcXVvdDtidXR0b24tYXJlYSZxdW90OyZndDsKICAgICAgICAmbHQ7YnV0dG9uIGlkPSZxdW90O3Jlc2V0QnV0dG9uJnF1b3Q7Jmd0O/CflIQg5paw55qE5LiA5bGAJmx0Oy9idXR0b24mZ3Q7CiAgICAmbHQ7L2RpdiZndDsKICAgICZsdDtkaXYgY2xhc3M9JnF1b3Q7aGludCZxdW90OyZndDsKICAgICAgICAmbHQ7aSZndDvwn5CtIOm8oOagh+enu+WKqOaOpeS9j+aYn+aYnyAmbWlkZG90OyDmvI/mjonkuInmrKHlsLHnu5PmnZ/llaYmbHQ7L2kmZ3Q7CiAgICAmbHQ7L2RpdiZndDsKICAgICZsdDtmb290ZXImZ3Q78J+luiBE6ICB5biI5LiT5bGeICZtaWRkb3Q7IOimgeW8gOW/g+WTpiZsdDsvZm9vdGVyJmd0OwombHQ7L2RpdiZndDsKCiZsdDtzY3JpcHQmZ3Q7CihmdW5jdGlvbigpIHsKICAgIC8vIC0tLS0tLS0tLS0g55S75biD5LiO6K6+572uIC0tLS0tLS0tLS0KICAgIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdnYW1lQ2FudmFzJyk7CiAgICBjb25zdCBjdHggPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnKTsKICAgIGNvbnN0IHNjb3JlU3BhbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzY29yZURpc3BsYXknKTsKICAgIGNvbnN0IG1pc3NTcGFuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ21pc3NEaXNwbGF5Jyk7CiAgICBjb25zdCByZXNldEJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdyZXNldEJ1dHRvbicpOwoKICAgIC8vIOWbuuWumuWwuuWvuAogICAgY29uc3QgVyA9IDQwMCwgSCA9IDQwMDsKICAgIGNhbnZhcy53aWR0aCA9IFc7IGNhbnZhcy5oZWlnaHQgPSBIOwoKICAgIC8vIOa4uOaIj+WPguaVsAogICAgY29uc3QgUEFERExFX1cgPSA4MDsgICAgICAgICAgICAgLy8g5p2/5a2Q5a695bqmCiAgICBjb25zdCBQQURETEVfSCA9IDEyOyAgICAgICAgICAgICAgLy8g5p2/5a2Q6auY5bqmCiAgICBjb25zdCBQQURETEVfWSA9IEggLSAzNTsgICAgICAgICAgIC8vIOadv+WtkHnlnZDmoIcgKOS4iui+uee8mCkKICAgIGNvbnN0IEJBTExfUkFESVVTID0gOTsgICAgICAgICAgICAgLy8g5pif5pif5aSn5bCPCiAgICBjb25zdCBNQVhfTUlTUyA9IDM7ICAgICAgICAgICAgICAgIC8vIOacgOWkp+a8j+eQg+asoeaVsAoKICAgIC8vIOWFqOWxgOWPmOmHjwogICAgbGV0IHBhZGRsZVggPSBXIC8gMjsgICAgICAgICAgICAgICAvLyDmnb/lrZDkuK3lv4N4CiAgICBsZXQgc2NvcmUgPSAwOwogICAgbGV0IG1pc3NDb3VudCA9IDA7CiAgICBsZXQgZ2FtZUFjdGl2ZSA9IHRydWU7ICAgICAgICAgICAgIC8vIOa4uOaIj+aYr+WQpui/kOihjAogICAgbGV0IGFuaW1hdGlvbklkID0gbnVsbDsKCiAgICAvLyDmmJ/mmJ/mlbDnu4QgKOWbuuWumjPpopfmmJ/mmJ8pCiAgICBsZXQgYmFsbHMgPSBbXTsKCiAgICAvLyAtLS0tLS0tLS0tIOi+heWKqeWHveaVsCAtLS0tLS0tLS0tCiAgICBmdW5jdGlvbiByYW5kb20obWluLCBtYXgpIHsKICAgICAgICByZXR1cm4gTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4pICsgbWluOwogICAgfQoKICAgIC8vIOmHjee9ruS4gOmil+aYn+aYn+eahOWxnuaApyAo5L2N572u572u6aG2LCDpmo/mnLrpgJ/luqYpCiAgICBmdW5jdGlvbiByZXNldEJhbGwoYmFsbCkgewogICAgICAgIGJhbGwueCA9IHJhbmRvbShCQUxMX1JBRElVUywgVyAtIEJBTExfUkFESVVTKTsKICAgICAgICBiYWxsLnkgPSBCQUxMX1JBRElVUyArIE1hdGgucmFuZG9tKCkgKiA1MDsgIC8vIOS7jumdoOi/kemhtumDqOW8gOWniwogICAgICAgIGJhbGwuc3BlZWRZID0gcmFuZG9tKDEuOCwgMy41KTsgICAgICAgICAgICAgIC8vIOS4i+iQvemAn+W6pumaj+acugogICAgICAgIC8vIOWKoOS4gOeCueWwj+Wwj+minOiJsuWPmOWMliAo6ZqP5py6KQogICAgICAgIGJhbGwuY29sb3IgPSBgaHNsKCR7TWF0aC5yYW5kb20oKSozMCArIDQwfSwgODUlLCA3MCUpYDsgLy8g5pqW6Imy5pif5pifCiAgICB9CgogICAgLy8g5Yid5aeL5YyW5LiJ6aKX5pif5pifCiAgICBmdW5jdGlvbiBpbml0QmFsbHMoKSB7CiAgICAgICAgYmFsbHMgPSBbXTsKICAgICAgICBmb3IgKGxldCBpID0gMDsgaSAmbHQ7IDM7IGkrKykgewogICAgICAgICAgICBsZXQgYmFsbCA9IHsKICAgICAgICAgICAgICAgIHg6IHJhbmRvbShCQUxMX1JBRElVUywgVyAtIEJBTExfUkFESVVTKSwKICAgICAgICAgICAgICAgIHk6IHJhbmRvbShCQUxMX1JBRElVUywgMTUwKSwgICAgICAgICAgLy8g5Yid5aeL5YiG5biD6Z2g5LiKCiAgICAgICAgICAgICAgICBzcGVlZFk6IHJhbmRvbSgxLjgsIDMuNSksCiAgICAgICAgICAgICAgICByYWRpdXM6IEJBTExfUkFESVVTLAogICAgICAgICAgICB9OwogICAgICAgICAgICBiYWxsLmNvbG9yID0gYGhzbCgke3JhbmRvbSg0MCwgNjApfSwgODUlLCA3MCUpYDsgLy8g6YeRL+apmeiJsuezuwogICAgICAgICAgICBiYWxscy5wdXNoKGJhbGwpOwogICAgICAgIH0KICAgIH0KICAgIGluaXRCYWxscygpOyAgLy8g5Yid5aeL6LCD55SoCgogICAgLy8g5pu05pawVUnliIbmlbAv55Sf5ZG9CiAgICBmdW5jdGlvbiB1cGRhdGVTdGF0cygpIHsKICAgICAgICBzY29yZVNwYW4udGV4dENvbnRlbnQgPSBzY29yZTsKICAgICAgICBtaXNzU3Bhbi50ZXh0Q29udGVudCA9IE1BWF9NSVNTIC0gbWlzc0NvdW50OyAgLy8g5pi+56S65Ymp5L2Z55Sf5ZG9CiAgICB9CgogICAgLy8gLS0tLS0tLS0tLSDnu5jliLYgLS0tLS0tLS0tLQogICAgZnVuY3Rpb24gZHJhd1BhZGRsZSgpIHsKICAgICAgICBjdHguc2hhZG93Q29sb3IgPSAnI2ZmZTNiMCc7CiAgICAgICAgY3R4LnNoYWRvd0JsdXIgPSAxMjsKICAgICAgICBjdHguYmVnaW5QYXRoKCk7CiAgICAgICAgY3R4LnJvdW5kUmVjdChwYWRkbGVYIC0gUEFERExFX1cvMiwgUEFERExFX1ksIFBBRERMRV9XLCBQQURETEVfSCwgMjApOwogICAgICAgIGN0eC5maWxsU3R5bGUgPSAnI2ZkYmI5ZSc7CiAgICAgICAgY3R4LmZpbGwoKTsKICAgICAgICAvLyDliqDpq5jlhYkKICAgICAgICBjdHguc2hhZG93Qmx1ciA9IDg7CiAgICAgICAgY3R4LmZpbGxTdHlsZSA9ICcjZmZlOWM0JzsKICAgICAgICBjdHguYmVnaW5QYXRoKCk7CiAgICAgICAgY3R4LnJvdW5kUmVjdChwYWRkbGVYIC0gUEFERExFX1cvMiArIDIsIFBBRERMRV9ZIC0gMSwgUEFERExFX1ctNCwgNCwgMTApOwogICAgICAgIGN0eC5maWxsKCk7CiAgICAgICAgLy8g6YeN572u6Zi05b2xCiAgICAgICAgY3R4LnNoYWRvd0JsdXIgPSAwOwogICAgfQoKICAgIC8vIOWwgeijheWchuinkuefqeW9ogogICAgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJELnByb3RvdHlwZS5yb3VuZFJlY3QgPSBmdW5jdGlvbih4LCB5LCB3LCBoLCByKSB7CiAgICAgICAgaWYgKHcgJmx0OyAyICogcikgciA9IHcgLyAyOwogICAgICAgIGlmIChoICZsdDsgMiAqIHIpIHIgPSBoIC8gMjsKICAgICAgICB0aGlzLm1vdmVUbyh4ICsgciwgeSk7CiAgICAgICAgdGhpcy5saW5lVG8oeCArIHcgLSByLCB5KTsKICAgICAgICB0aGlzLnF1YWRyYXRpY0N1cnZlVG8oeCArIHcsIHksIHggKyB3LCB5ICsgcik7CiAgICAgICAgdGhpcy5saW5lVG8oeCArIHcsIHkgKyBoIC0gcik7CiAgICAgICAgdGhpcy5xdWFkcmF0aWNDdXJ2ZVRvKHggKyB3LCB5ICsgaCwgeCArIHcgLSByLCB5ICsgaCk7CiAgICAgICAgdGhpcy5saW5lVG8oeCArIHIsIHkgKyBoKTsKICAgICAgICB0aGlzLnF1YWRyYXRpY0N1cnZlVG8oeCwgeSArIGgsIHgsIHkgKyBoIC0gcik7CiAgICAgICAgdGhpcy5saW5lVG8oeCwgeSArIHIpOwogICAgICAgIHRoaXMucXVhZHJhdGljQ3VydmVUbyh4LCB5LCB4ICsgciwgeSk7CiAgICAgICAgcmV0dXJuIHRoaXM7CiAgICB9OwoKICAgIGZ1bmN0aW9uIGRyYXdCYWxscygpIHsKICAgICAgICBmb3IgKGxldCBiIG9mIGJhbGxzKSB7CiAgICAgICAgICAgIC8vIOaYn+aYn+WPkeWFieaViOaenAogICAgICAgICAgICBjdHguc2hhZG93Q29sb3IgPSAnI2ZkZWJiMyc7CiAgICAgICAgICAgIGN0eC5zaGFkb3dCbHVyID0gMTU7CiAgICAgICAgICAgIGN0eC5iZWdpblBhdGgoKTsKICAgICAgICAgICAgY3R4LmFyYyhiLngsIGIueSwgYi5yYWRpdXMsIDAsIE1hdGguUEkgKiAyKTsKICAgICAgICAgICAgY3R4LmZpbGxTdHlsZSA9IGIuY29sb3IgfHwgJyNmOGNlN2UnOwogICAgICAgICAgICBjdHguZmlsbCgpOwogICAgICAgICAgICAvLyDlsI/pq5jlhYkKICAgICAgICAgICAgY3R4LnNoYWRvd0JsdXIgPSA0OwogICAgICAgICAgICBjdHguYmVnaW5QYXRoKCk7CiAgICAgICAgICAgIGN0eC5hcmMoYi54LTIsIGIueS0yLCA0LCAwLCBNYXRoLlBJKjIpOwogICAgICAgICAgICBjdHguZmlsbFN0eWxlID0gJyNmZmZkZTcnOwogICAgICAgICAgICBjdHguZmlsbCgpOwogICAgICAgIH0KICAgICAgICBjdHguc2hhZG93Qmx1ciA9IDA7CiAgICB9CgogICAgZnVuY3Rpb24gZHJhd0dhbWVPdmVyKCkgewogICAgICAgIGN0eC5mb250ID0gJ2JvbGQgMzJweCAmcXVvdDtTZWdvZSBVSSZxdW90OywgJnF1b3Q7Um9ib3RvJnF1b3Q7LCBzYW5zLXNlcmlmJzsKICAgICAgICBjdHguZmlsbFN0eWxlID0gJyNmZmRkYmInOwogICAgICAgIGN0eC5zaGFkb3dDb2xvciA9ICcjYWI0N2JjJzsKICAgICAgICBjdHguc2hhZG93Qmx1ciA9IDIwOwogICAgICAgIGN0eC5saW5lV2lkdGggPSAyOwogICAgICAgIGN0eC5zdHJva2VTdHlsZSA9ICcjMmEwZjNhJzsKICAgICAgICBjdHgudGV4dEFsaWduID0gJ2NlbnRlcic7CiAgICAgICAgY3R4LnN0cm9rZVRleHQoJ/CfkpQgR0FNRSBPVkVSJywgVy8yLCBILzItMjApOwogICAgICAgIGN0eC5maWxsVGV4dCgn8J+SlCBHQU1FIE9WRVInLCBXLzIsIEgvMi0yMCk7CiAgICAgICAgY3R4LmZvbnQgPSAnYm9sZCAyMnB4IHNhbnMtc2VyaWYnOwogICAgICAgIGN0eC5maWxsU3R5bGUgPSAnI2ZmYmM3YSc7CiAgICAgICAgY3R4LmZpbGxUZXh0KCfngrnkuIvmlrnmjInpkq7lho3mnaXkuIDlsYAnLCBXLzIsIEgvMiArIDM1KTsKICAgICAgICBjdHguc2hhZG93Qmx1ciA9IDA7CiAgICB9CgogICAgLy8gLS0tLS0tLS0tLSDmuLjmiI/pgLvovpHmm7TmlrAgKOavj+W4pykgLS0tLS0tLS0tLQogICAgZnVuY3Rpb24gdXBkYXRlR2FtZSgpIHsKICAgICAgICBpZiAoIWdhbWVBY3RpdmUpIHJldHVybjsgIC8vIOa4uOaIj+e7k+adn++8jOS4jeabtOaWsOmAu+i+ke+8jOWPqui0n+i0o+e7mOWItiAo55Sx5aSW6YOo5o6n5Yi2KQoKICAgICAgICBmb3IgKGxldCBiYWxsIG9mIGJhbGxzKSB7CiAgICAgICAgICAgIC8vIOenu+WKqOaYn+aYnwogICAgICAgICAgICBiYWxsLnkgKz0gYmFsbC5zcGVlZFk7CgogICAgICAgICAgICAvLyAxLiDlhYjmo4DmtYvmmK/lkKbmvI/lh7rlupXpg6ggKOi2heWHuueUu+W4gykKICAgICAgICAgICAgaWYgKGJhbGwueSArIGJhbGwucmFkaXVzICZndDs9IEgpIHsKICAgICAgICAgICAgICAgIC8vIOa8j+aOieS4gOmilwogICAgICAgICAgICAgICAgbWlzc0NvdW50Kys7CiAgICAgICAgICAgICAgICBpZiAobWlzc0NvdW50ICZndDs9IE1BWF9NSVNTKSB7CiAgICAgICAgICAgICAgICAgICAgZ2FtZUFjdGl2ZSA9IGZhbHNlOyAgIC8vIOa4uOaIj+e7k+adnwogICAgICAgICAgICAgICAgICAgIC8vIOeri+WIu+abtOaWsFVJ55Sf5ZG95YC8CiAgICAgICAgICAgICAgICAgICAgdXBkYXRlU3RhdHMoKTsKICAgICAgICAgICAgICAgICAgICAvLyDkuI3lho3nu6fnu63lpITnkIblhbbku5bnkIMgKOebtOaOpei3s+WHuuW+queOrykKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIC8vIOmHjee9rui/memil+aYn+aYnyAo5Zue5Yiw6aG26YOoKQogICAgICAgICAgICAgICAgcmVzZXRCYWxsKGJhbGwpOwogICAgICAgICAgICAgICAgdXBkYXRlU3RhdHMoKTsKICAgICAgICAgICAgICAgIGNvbnRpbnVlOyAgLy8g6Lez6L+H5p2/5a2Q56Kw5pKe5qOA5rWLICjlt7Lnu4/ph43nva4pCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC8vIDIuIOajgOa1i+aYr+WQpuiiq+adv+WtkOaOpeS9jyAo5LiU5ri45oiP6L+Y5Zyo6L+b6KGMKQogICAgICAgICAgICAvLyDmnaHku7Y6IOaYn+aYn+S4i+e8mOinpueisOWIsOadv+WtkOS4iue8mO+8jOW5tuS4lOaYn+aYn+S4reW/g+WcqOadv+WtkOWuveW6puWGhSAo5LiU5pif5pif6L+Y5rKh5a6M5YWo56m/6L+H5p2/5a2QKQogICAgICAgICAgICBpZiAoYmFsbC55ICsgYmFsbC5yYWRpdXMgJmd0Oz0gUEFERExFX1kgJmFtcDsmYW1wOyBiYWxsLnkgLSBiYWxsLnJhZGl1cyAmbHQ7PSBQQURETEVfWSArIFBBRERMRV9IKSB7CiAgICAgICAgICAgICAgICBpZiAoYmFsbC54ICZndDs9IHBhZGRsZVggLSBQQURETEVfVy8yICZhbXA7JmFtcDsgYmFsbC54ICZsdDs9IHBhZGRsZVggKyBQQURETEVfVy8yKSB7CiAgICAgICAgICAgICAgICAgICAgLy8g5o6l5L2P5ZWm77yB5Yqg5YiGCiAgICAgICAgICAgICAgICAgICAgc2NvcmUrKzsKICAgICAgICAgICAgICAgICAgICAvLyDph43nva7ov5npopfmmJ/mmJ/liLDpobbpg6gKICAgICAgICAgICAgICAgICAgICByZXNldEJhbGwoYmFsbCk7CiAgICAgICAgICAgICAgICAgICAgdXBkYXRlU3RhdHMoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy8g5YaN5qyh5qOA5p+l5Zug5Li65ryP55CD5Y+v6IO95a+86Ie05ri45oiP57uT5p2fCiAgICAgICAgaWYgKG1pc3NDb3VudCAmZ3Q7PSBNQVhfTUlTUykgewogICAgICAgICAgICBnYW1lQWN0aXZlID0gZmFsc2U7CiAgICAgICAgfQogICAgICAgIC8vIOabtOaWsFVJICjkv53pmakpCiAgICAgICAgdXBkYXRlU3RhdHMoKTsKICAgIH0KCiAgICAvLyAtLS0tLS0tLS0tIOa4suafk+S4u+W+queOryAtLS0tLS0tLS0tCiAgICBmdW5jdGlvbiByZW5kZXIoKSB7CiAgICAgICAgY3R4LmNsZWFyUmVjdCgwLCAwLCBXLCBIKTsKCiAgICAgICAgLy8g57uY5Yi25pif56m66IOM5pmvICjoo4XppbApCiAgICAgICAgY3R4LmZpbGxTdHlsZSA9ICcjMjIxNTMzJzsKICAgICAgICBjdHguZmlsbFJlY3QoMCwgMCwgVywgSCk7CiAgICAgICAgZm9yIChsZXQgaT0wOyBpJmx0Ozg7IGkrKykgewogICAgICAgICAgICBjdHguZmlsbFN0eWxlID0gYHJnYmEoMjU1LDI0MCwxODAsJHswLjErTWF0aC5yYW5kb20oKSowLjF9KWA7CiAgICAgICAgICAgIGN0eC5iZWdpblBhdGgoKTsKICAgICAgICAgICAgY3R4LmFyYygzMCsgaSo0NywgKGkqMzMpJUgsIDIsIDAsIE1hdGguUEkqMik7CiAgICAgICAgICAgIGN0eC5maWxsKCk7CiAgICAgICAgfQoKICAgICAgICAvLyDnu5jliLbmnb/lrZDkuI7mmJ/mmJ8gKOaXoOiuuua4uOaIj+aYr+WQpua/gOa0u+mDvee7mOWItikKICAgICAgICBkcmF3UGFkZGxlKCk7CiAgICAgICAgZHJhd0JhbGxzKCk7CgogICAgICAgIC8vIOWmguaenOa4uOaIj+e7k+adn++8jOaYvuekuumBrue9qeaWh+WtlwogICAgICAgIGlmICghZ2FtZUFjdGl2ZSkgewogICAgICAgICAgICBjdHguZmlsbFN0eWxlID0gJ3JnYmEoMjAsIDUsIDI1LCAwLjcpJzsKICAgICAgICAgICAgY3R4LmZpbGxSZWN0KDAsIDAsIFcsIEgpOwogICAgICAgICAgICBkcmF3R2FtZU92ZXIoKTsKICAgICAgICB9CgogICAgICAgIC8vIOe7p+e7reS4i+S4gOW4p+WKqOeUuyAo5Y+q6KaB5rKh5pyJ5Li75Yqo5Y+W5raIKQogICAgICAgIGlmIChnYW1lQWN0aXZlKSB7CiAgICAgICAgICAgIHVwZGF0ZUdhbWUoKTsgICAvLyDlhYjmm7TmlrDpgLvovpHlho3or7fmsYLkuIvkuIDluKcKICAgICAgICAgICAgYW5pbWF0aW9uSWQgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUocmVuZGVyKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAvLyDmuLjmiI/nu5PmnZ/kvYbkv53mjIHpnZnmgIHmuLLmn5MgKOWBnOWcqOe7k+adn+eUu+mdoiksIOS4jeWGjee7p+e7reivt+axguWKqOeUu++8jOiKguecgei1hOa6kAogICAgICAgICAgICBpZiAoYW5pbWF0aW9uSWQpIHsKICAgICAgICAgICAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKGFuaW1hdGlvbklkKTsKICAgICAgICAgICAgICAgIGFuaW1hdGlvbklkID0gbnVsbDsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvLyAtLS0tLS0tLS0tIOmHjee9rua4uOaIjyAtLS0tLS0tLS0tCiAgICBmdW5jdGlvbiByZXNldEdhbWUoKSB7CiAgICAgICAgLy8g5aaC5p6c5bey5pyJ5Yqo55S777yM5YWI5Y+W5raIICjpgb/lhY3lpJrkuKrlvqrnjq8pCiAgICAgICAgaWYgKGFuaW1hdGlvbklkKSB7CiAgICAgICAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKGFuaW1hdGlvbklkKTsKICAgICAgICAgICAgYW5pbWF0aW9uSWQgPSBudWxsOwogICAgICAgIH0KCiAgICAgICAgLy8g6YeN572u5Y+Y6YePCiAgICAgICAgc2NvcmUgPSAwOwogICAgICAgIG1pc3NDb3VudCA9IDA7CiAgICAgICAgZ2FtZUFjdGl2ZSA9IHRydWU7CiAgICAgICAgcGFkZGxlWCA9IFcvMjsgICAvLyDmnb/lrZDlvZLkuK0KCiAgICAgICAgLy8g6YeN572u5LiJ6aKX5pif5pifICjlhajpg6jnva7pobYsIOmaj+acuumAn+W6pikKICAgICAgICBpbml0QmFsbHMoKTsKCiAgICAgICAgLy8g5pu05pawVUkKICAgICAgICB1cGRhdGVTdGF0cygpOwoKICAgICAgICAvLyDph43mlrDlkK/liqjmuLLmn5Plvqrnjq8KICAgICAgICBhbmltYXRpb25JZCA9IHJlcXVlc3RBbmltYXRpb25GcmFtZShyZW5kZXIpOwogICAgfQoKICAgIC8vIC0tLS0tLS0tLS0g6byg5qCHIC8g6Kem5pG4IOaOp+WItuadv+WtkCAtLS0tLS0tLS0tCiAgICBmdW5jdGlvbiBoYW5kbGVNb3ZlKGNsaWVudFgsIGNsaWVudFkpIHsKICAgICAgICBpZiAoIWNhbnZhcykgcmV0dXJuOwogICAgICAgIGNvbnN0IHJlY3QgPSBjYW52YXMuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7CiAgICAgICAgLy8g6K6h566X6byg5qCH5ZyoY2FudmFz5YaF55qE55u45a+55Z2Q5qCHICgwfjEpCiAgICAgICAgbGV0IHNjYWxlWCA9IChjbGllbnRYIC0gcmVjdC5sZWZ0KSAvIHJlY3Qud2lkdGg7CiAgICAgICAgLy8g6L6555WM6ZmQ5Yi2CiAgICAgICAgbGV0IG5ld1BhZGRsZVggPSBNYXRoLm1heChQQURETEVfVy8yLCBNYXRoLm1pbihXIC0gUEFERExFX1cvMiwgc2NhbGVYICogVykpOwogICAgICAgIHBhZGRsZVggPSBuZXdQYWRkbGVYOwogICAgfQoKICAgIC8vIOm8oOagh+enu+WKqAogICAgY2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIChlKSA9Jmd0OyB7CiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgICAgIGhhbmRsZU1vdmUoZS5jbGllbnRYLCBlLmNsaWVudFkpOwogICAgfSk7CgogICAgLy8g6Kem5pG456e75YqoICjnp7vliqjnq68pCiAgICBjYW52YXMuYWRkRXZlbnRMaXN0ZW5lcigndG91Y2htb3ZlJywgKGUpID0mZ3Q7IHsKICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7CiAgICAgICAgaWYgKGUudG91Y2hlcy5sZW5ndGggJmd0OyAwKSB7CiAgICAgICAgICAgIGhhbmRsZU1vdmUoZS50b3VjaGVzWzBdLmNsaWVudFgsIGUudG91Y2hlc1swXS5jbGllbnRZKTsKICAgICAgICB9CiAgICB9LCB7IHBhc3NpdmU6IGZhbHNlIH0pOwoKICAgIC8vIOinpuaRuOW8gOWni+S5n+imgemYu+atoum7mOiupCAo6Ziy5q2i6aG16Z2i5rua5YqoKQogICAgY2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCAoZSkgPSZndDsgZS5wcmV2ZW50RGVmYXVsdCgpLCB7IHBhc3NpdmU6IGZhbHNlIH0pOwoKICAgIC8vIOm8oOagh+emu+W8gGNhbnZhc+WMuuWfn+aXtuS4jeWBmueJueauiuWkhOeQhiAo5p2/5a2Q5YGc5Zyo5pyA5ZCO5L2N572uKQoKICAgIC8vIOmHjee9ruaMiemSrgogICAgcmVzZXRCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9Jmd0OyB7CiAgICAgICAgcmVzZXRHYW1lKCk7CiAgICB9KTsKCiAgICAvLyDkuLrkuobpmLLmraLpvKDmoIfliJrnp7vlhaXml7bmnb/lrZDot7Plj5jvvIzliJ3lp4vljJbkuIDmrKFyZW5kZXIKICAgIC8vIOW8gOWni+a4uOaIj+W+queOrwogICAgcmVzZXRHYW1lKCk7ICAvLyDpobrkvr/lsLHlkK/liqjkuoYKCiAgICAvLyDnqpflj6PlpLHljrvnhKbngrnml7bllaXkuZ/kuI3lgZrvvIzkv53nlZnmuLjmiI8gKOayoeWFs+ezuykKfSkoKTsKJmx0Oy9zY3JpcHQmZ3Q7CiZsdDsvYm9keSZndDsKJmx0Oy9odG1sJmd0Ow==
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>D老师的接星星小游戏 ✨</title>
<style>
body {
background: linear-gradient(145deg, #2b1b3a, #1a1125);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
margin: 0;
padding: 10px;
}
.game-container {
background: #3a2a4a;
padding: 25px 20px 20px 20px;
border-radius: 48px;
box-shadow: 0 20px 30px rgba(0,0,0,0.6), inset 0 2px 4px rgba(255,255,255,0.1);
border: 1px solid #9578b0;
}
h2 {
text-align: center;
margin: 0 0 12px 0;
font-size: 32px;
font-weight: 600;
color: #ffde9e;
text-shadow: 0 4px 0 #8b5f9c, 0 6px 10px black;
letter-spacing: 2px;
word-break: keep-all;
}
.stats {
display: flex;
justify-content: space-between;
background: #1e132b;
padding: 12px 20px;
border-radius: 60px;
margin-bottom: 15px;
color: #fadf9e;
font-weight: bold;
font-size: 20px;
border: 2px solid #b494d0;
box-shadow: inset 0 2px 5px #0b0810;
}
.stats span {
background: #2d1e3a;
padding: 5px 20px;
border-radius: 40px;
color: #ffd966;
font-size: 24px;
border: 1px solid #c9a9f0;
}
canvas {
display: block;
margin: 0 auto;
border-radius: 28px;
background: #1e1529;
box-shadow: 0 0 0 3px #b28ad0, 0 15px 20px black;
cursor: none; /* 隐藏默认鼠标,更像游戏 */
touch-action: none; /* 防止触摸时滚动/缩放 */
width: 100%;
max-width: 400px;
height: auto;
}
.button-area {
display: flex;
justify-content: center;
margin-top: 20px;
}
button {
background: #e4b1fe;
border: none;
font-size: 28px;
font-weight: bold;
padding: 12px 40px;
border-radius: 50px;
color: #2f174b;
box-shadow: 0 9px 0 #6b3f8b, 0 10px 20px black;
transition: 0.07s ease;
border: 2px solid #ffdcaa;
cursor: pointer;
}
button:active {
transform: translateY(7px);
box-shadow: 0 2px 0 #6b3f8b, 0 8px 12px black;
}
.hint {
text-align: center;
color: #cdb5e9;
margin-top: 12px;
font-size: 16px;
font-weight: 500;
text-shadow: 0 2px 3px black;
}
.hint i {
font-style: normal;
background: #2f1d41;
padding: 4px 10px;
border-radius: 99px;
}
footer {
text-align: center;
color: #7b6293;
margin-top: 5px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="game-container">
<h2>⭐ D老师 · 星星接接乐 ⭐</h2>
<div class="stats">
<div>✨ 得分</div>
<span id="scoreDisplay">0</span>
<div>❤️ 生命</div>
<span id="missDisplay">3</span>
</div>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div class="button-area">
<button id="resetButton">🔄 新的一局</button>
</div>
<div class="hint">
<i>🐭 鼠标移动接住星星 · 漏掉三次就结束啦</i>
</div>
<footer>🥺 D老师专属 · 要开心哦</footer>
</div>
<script>
(function() {
// ---------- 画布与设置 ----------
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreSpan = document.getElementById('scoreDisplay');
const missSpan = document.getElementById('missDisplay');
const resetBtn = document.getElementById('resetButton');
// 固定尺寸
const W = 400, H = 400;
canvas.width = W; canvas.height = H;
// 游戏参数
const PADDLE_W = 80; // 板子宽度
const PADDLE_H = 12; // 板子高度
const PADDLE_Y = H - 35; // 板子y坐标 (上边缘)
const BALL_RADIUS = 9; // 星星大小
const MAX_MISS = 3; // 最大漏球次数
// 全局变量
let paddleX = W / 2; // 板子中心x
let score = 0;
let missCount = 0;
let gameActive = true; // 游戏是否运行
let animationId = null;
// 星星数组 (固定3颗星星)
let balls = [];
// ---------- 辅助函数 ----------
function random(min, max) {
return Math.random() * (max - min) + min;
}
// 重置一颗星星的属性 (位置置顶, 随机速度)
function resetBall(ball) {
ball.x = random(BALL_RADIUS, W - BALL_RADIUS);
ball.y = BALL_RADIUS + Math.random() * 50; // 从靠近顶部开始
ball.speedY = random(1.8, 3.5); // 下落速度随机
// 加一点小小颜色变化 (随机)
ball.color = `hsl(${Math.random()*30 + 40}, 85%, 70%)`; // 暖色星星
}
// 初始化三颗星星
function initBalls() {
balls = [];
for (let i = 0; i < 3; i++) {
let ball = {
x: random(BALL_RADIUS, W - BALL_RADIUS),
y: random(BALL_RADIUS, 150), // 初始分布靠上
speedY: random(1.8, 3.5),
radius: BALL_RADIUS,
};
ball.color = `hsl(${random(40, 60)}, 85%, 70%)`; // 金/橙色系
balls.push(ball);
}
}
initBalls(); // 初始调用
// 更新UI分数/生命
function updateStats() {
scoreSpan.textContent = score;
missSpan.textContent = MAX_MISS - missCount; // 显示剩余生命
}
// ---------- 绘制 ----------
function drawPaddle() {
ctx.shadowColor = '#ffe3b0';
ctx.shadowBlur = 12;
ctx.beginPath();
ctx.roundRect(paddleX - PADDLE_W/2, PADDLE_Y, PADDLE_W, PADDLE_H, 20);
ctx.fillStyle = '#fdbb9e';
ctx.fill();
// 加高光
ctx.shadowBlur = 8;
ctx.fillStyle = '#ffe9c4';
ctx.beginPath();
ctx.roundRect(paddleX - PADDLE_W/2 + 2, PADDLE_Y - 1, PADDLE_W-4, 4, 10);
ctx.fill();
// 重置阴影
ctx.shadowBlur = 0;
}
// 封装圆角矩形
CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
this.moveTo(x + r, y);
this.lineTo(x + w - r, y);
this.quadraticCurveTo(x + w, y, x + w, y + r);
this.lineTo(x + w, y + h - r);
this.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
this.lineTo(x + r, y + h);
this.quadraticCurveTo(x, y + h, x, y + h - r);
this.lineTo(x, y + r);
this.quadraticCurveTo(x, y, x + r, y);
return this;
};
function drawBalls() {
for (let b of balls) {
// 星星发光效果
ctx.shadowColor = '#fdebb3';
ctx.shadowBlur = 15;
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2);
ctx.fillStyle = b.color || '#f8ce7e';
ctx.fill();
// 小高光
ctx.shadowBlur = 4;
ctx.beginPath();
ctx.arc(b.x-2, b.y-2, 4, 0, Math.PI*2);
ctx.fillStyle = '#fffde7';
ctx.fill();
}
ctx.shadowBlur = 0;
}
function drawGameOver() {
ctx.font = 'bold 32px "Segoe UI", "Roboto", sans-serif';
ctx.fillStyle = '#ffddbb';
ctx.shadowColor = '#ab47bc';
ctx.shadowBlur = 20;
ctx.lineWidth = 2;
ctx.strokeStyle = '#2a0f3a';
ctx.textAlign = 'center';
ctx.strokeText('💔 GAME OVER', W/2, H/2-20);
ctx.fillText('💔 GAME OVER', W/2, H/2-20);
ctx.font = 'bold 22px sans-serif';
ctx.fillStyle = '#ffbc7a';
ctx.fillText('点下方按钮再来一局', W/2, H/2 + 35);
ctx.shadowBlur = 0;
}
// ---------- 游戏逻辑更新 (每帧) ----------
function updateGame() {
if (!gameActive) return; // 游戏结束,不更新逻辑,只负责绘制 (由外部控制)
for (let ball of balls) {
// 移动星星
ball.y += ball.speedY;
// 1. 先检测是否漏出底部 (超出画布)
if (ball.y + ball.radius >= H) {
// 漏掉一颗
missCount++;
if (missCount >= MAX_MISS) {
gameActive = false; // 游戏结束
// 立刻更新UI生命值
updateStats();
// 不再继续处理其他球 (直接跳出循环)
break;
}
// 重置这颗星星 (回到顶部)
resetBall(ball);
updateStats();
continue; // 跳过板子碰撞检测 (已经重置)
}
// 2. 检测是否被板子接住 (且游戏还在进行)
// 条件: 星星下缘触碰到板子上缘,并且星星中心在板子宽度内 (且星星还没完全穿过板子)
if (ball.y + ball.radius >= PADDLE_Y && ball.y - ball.radius <= PADDLE_Y + PADDLE_H) {
if (ball.x >= paddleX - PADDLE_W/2 && ball.x <= paddleX + PADDLE_W/2) {
// 接住啦!加分
score++;
// 重置这颗星星到顶部
resetBall(ball);
updateStats();
}
}
}
// 再次检查因为漏球可能导致游戏结束
if (missCount >= MAX_MISS) {
gameActive = false;
}
// 更新UI (保险)
updateStats();
}
// ---------- 渲染主循环 ----------
function render() {
ctx.clearRect(0, 0, W, H);
// 绘制星空背景 (装饰)
ctx.fillStyle = '#221533';
ctx.fillRect(0, 0, W, H);
for (let i=0; i<8; i++) {
ctx.fillStyle = `rgba(255,240,180,${0.1+Math.random()*0.1})`;
ctx.beginPath();
ctx.arc(30+ i*47, (i*33)%H, 2, 0, Math.PI*2);
ctx.fill();
}
// 绘制板子与星星 (无论游戏是否激活都绘制)
drawPaddle();
drawBalls();
// 如果游戏结束,显示遮罩文字
if (!gameActive) {
ctx.fillStyle = 'rgba(20, 5, 25, 0.7)';
ctx.fillRect(0, 0, W, H);
drawGameOver();
}
// 继续下一帧动画 (只要没有主动取消)
if (gameActive) {
updateGame(); // 先更新逻辑再请求下一帧
animationId = requestAnimationFrame(render);
} else {
// 游戏结束但保持静态渲染 (停在结束画面), 不再继续请求动画,节省资源
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
}
}
// ---------- 重置游戏 ----------
function resetGame() {
// 如果已有动画,先取消 (避免多个循环)
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
// 重置变量
score = 0;
missCount = 0;
gameActive = true;
paddleX = W/2; // 板子归中
// 重置三颗星星 (全部置顶, 随机速度)
initBalls();
// 更新UI
updateStats();
// 重新启动渲染循环
animationId = requestAnimationFrame(render);
}
// ---------- 鼠标 / 触摸 控制板子 ----------
function handleMove(clientX, clientY) {
if (!canvas) return;
const rect = canvas.getBoundingClientRect();
// 计算鼠标在canvas内的相对坐标 (0~1)
let scaleX = (clientX - rect.left) / rect.width;
// 边界限制
let newPaddleX = Math.max(PADDLE_W/2, Math.min(W - PADDLE_W/2, scaleX * W));
paddleX = newPaddleX;
}
// 鼠标移动
canvas.addEventListener('mousemove', (e) => {
e.preventDefault();
handleMove(e.clientX, e.clientY);
});
// 触摸移动 (移动端)
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
if (e.touches.length > 0) {
handleMove(e.touches[0].clientX, e.touches[0].clientY);
}
}, { passive: false });
// 触摸开始也要阻止默认 (防止页面滚动)
canvas.addEventListener('touchstart', (e) => e.preventDefault(), { passive: false });
// 鼠标离开canvas区域时不做特殊处理 (板子停在最后位置)
// 重置按钮
resetBtn.addEventListener('click', () => {
resetGame();
});
// 为了防止鼠标刚移入时板子跳变,初始化一次render
// 开始游戏循环
resetGame(); // 顺便就启动了
// 窗口失去焦点时啥也不做,保留游戏 (没关系)
})();
</script>
</body>
</html>