//=============================================================================
// Ignideus777_Snake
//=============================================================================

/*:
 * @target MZ
 * @plugindesc A dynamic Snake minigame with color customization and special levels.
 * @author Ignideus777
 * @version 2.3.0
 *
 * @help
 * -Snake minigame for RPG maker MZ.
 * -Uses only PIXI graphics.
 * -No image files needed
 * -Support Mouse and Keyboard.
 * -Can leave default for easy set up.
 * -Each minigame can be randomized.
 *
 * --- Controls And Gameplay ---
 * Keyboard: Use Arrow Keys to direct the snake.
 * Mouse: Move the cursor to direct the snake.
 * Pause: Press 'ctrl' key to pause.
 * Resume: While paused, press 'X', 'ESC', or Right-Click to resume.
 * Exit Paused Game: While paused, press 'ctrl' again to exit.
 *
 * --- Display ---
 * If the "Custom Background" parameter is left empty, the background will be
 * transparent, showing the map or scene underneath.
 *
 * --- Sound Effects ---
 * You can customize all sound effects in the plugin parameters. If a sound
 * is left blank, a default engine sound will be used.
 * 
 * @param --- Controls ---
 *
 * @param enableMouseSupport
 * @text Enable Mouse Support
 * @parent --- Controls ---
 * @desc If true, the "Mouse" option will be available on the selection screen.
 * @type boolean
 * @default true
 *
 * @param --- General Settings ---
 *
 * @param startSpeed
 * @text Default Starting Speed
 * @parent --- General Settings ---
 * @desc Frames per snake move. Lower is faster.
 * @type number
 * @decimals 2
 * @default 6
 *
 * @param scoreVariableId
 * @text Default Score Variable ID
 * @parent --- General Settings ---
 * @type variable
 * @default 1
 *
 * @param boardWidth
 * @text Default Board Width
 * @parent --- General Settings ---
 * @type number
 * @default 25
 *
 * @param boardHeight
 * @text Default Board Height
 * @parent --- General Settings ---
 * @type number
 * @default 20
 *
 * @param --- Display Settings ---
 *
 * @param defaultSnakeColor
 * @text Default Snake Color
 * @parent --- Display Settings ---
 * @desc The default color of the snake in hex format (e.g., #E1C16E).
 * @type string
 * @default #E1C16E
 *
 * @param backImage
 * @text Background File
 * @parent --- Display Settings ---
 * @desc Optional background image from the pictures folder. Automaticity stretches to fill the screen. Must png file type.
 * @type file
 * @dir img/pictures/
 * @default
 *
 * @param --- Board Outline Settings ---
 *
 * @param enableOutline
 * @text Enable Board Outline
 * @parent --- Board Outline Settings ---
 * @desc If true, an outline will be drawn around the board.
 * @type boolean
 * @default true
 *
 * @param outlineThickness
 * @text Outline Thickness
 * @parent --- Board Outline Settings ---
 * @desc The thickness of the board outline in pixels.
 * @type number
 * @min 1
 * @default 3
 *
 * @param outlineColor
 * @text Outline Color
 * @parent --- Board Outline Settings ---
 * @desc The color of the outline in hex format (e.g., #CCCCCC).
 * @type string
 * @default #CCCCCC
 *
 * @param outlineAlpha
 * @text Outline Opacity
 * @parent --- Board Outline Settings ---
 * @desc The transparency of the outline. 0.0 is invisible, 1.0 is opaque.
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0
 *
 * @param --- Gameplay Settings ---
 *
 * @param speedIncrement
 * @text Speed Increment
 * @parent --- Gameplay Settings ---
 * @desc How much to decrease the move timer by after eating a brown food.
 * @type number
 * @decimals 2
 * @default 0.15
 *
 * @param speedEffectDuration
 * @text Speed Effect Duration
 * @parent --- Gameplay Settings ---
 * @desc How long speed-up/slow-down effects last, in frames. 60 = 1 second.
 * @type number
 * @default 300
 *
 * @param speedUpFactor
 * @text Speed Up Factor
 * @parent --- Gameplay Settings ---
 * @desc Divides the speed delay. e.g., 2 makes the snake 2x faster.
 * @type number
 * @decimals 2
 * @min 1
 * @default 1.5
 *
 * @param slowDownFactor
 * @text Slow Down Factor
 * @parent --- Gameplay Settings ---
 * @desc Divides the speed delay. e.g., 0.5 makes the snake 2x slower.
 * @type number
 * @decimals 2
 * @min 0.01
 * @max 1
 * @default 0.35
 *
 * @param dietShrinkAmount
 * @text Diet Shrink Amount
 * @parent --- Gameplay Settings ---
 * @desc How many segments the Diet (pink) food removes.
 * @type number
 * @min 1
 * @default 3
 *
 * @param superFoodScore
 * @text Super Food Score
 * @parent --- Gameplay Settings ---
 * @desc Score for eating super (teal) food.
 * @type number @min 0 @default 5
 *
 * @param superFoodGrowth
 * @text Super Food Tail Growth
 * @parent --- Gameplay Settings ---
 * @desc How many segments the snake grows by from a super food.
 * @type number @min 1 @default 5
 *
 * @param speedUpFoodScore
 * @text Speed Up Food Score
 * @parent --- Gameplay Settings ---
 * @desc Score for eating speed-up (red) food. Also grows snake by 1.
 * @type number @min 0 @default 2
 *
 * @param slowDownFoodScore
 * @text Slow Down Food Score
 * @parent --- Gameplay Settings ---
 * @desc Score for eating slow-down (green) food. Also grows snake by 1.
 * @type number @min 0 @default 2
 *
 * @param dietFoodScore
 * @text Diet Food Score
 * @parent --- Gameplay Settings ---
 * @desc Score for eating diet (pink) food, which shrinks the snake.
 * @type number @min 0 @default 1
 *
 * @param barrierFoodScore
 * @text Barrier Food Score
 * @parent --- Gameplay Settings ---
 * @desc Score for eating barrier (white) food.
 * @type number @min 0 @default 1
 *
 * @param --- Food Spawn Chance (%) ---
 *
 * @param redFoodChance
 * @text Red Food (Speed Up)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 20
 *
 * @param greenFoodChance
 * @text Green Food (Slow Down)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 20
 *
 * @param purpleFoodChance
 * @text Teal Food (Super)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 10
 *
 * @param blackFoodChance
 * @text Black Food (Poison)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 15
 *
 * @param whiteFoodChance
 * @text White Food (Barrier)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 12
 *
 * @param pinkFoodChance
 * @text Pink Food (Diet)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 12
 *
 * @param --- Food Lifespan Settings ---
 * @desc Set how many main (brown) foods must be eaten before a special food disappears. Set to 0 to make it last forever.
 *
 * @param speedUpFoodLifespan
 * @text Speed-Up Food Lifespan
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 0
 *
 * @param slowDownFoodLifespan
 * @text Slow-Down Food Lifespan
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 0
 *
 * @param superFoodLifespan
 * @text Super Food Lifespan
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 2
 *
 * @param poisonFoodLifespan
 * @text Poison Food Lifespan
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 3
 *
 * @param barrierFoodLifespan
 * @text Barrier Food Lifespan
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 5
 *
 * @param dietFoodLifespan
 * @text Diet Food Lifespan
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 5
 *
 * @param --- Sound Effects ---
 * @desc Optional custom sound effects. If left blank, default sounds will be used.
 *
 * @param seConfirm
 * @text Confirm SE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Decision2
 *
 * @param seCursor
 * @text Cursor SE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Cursor3
 *
 * @param seEat
 * @text Eat Food SE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Heal1
 *
 * @param seGameOver
 * @text Game Over SE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Buzzer1
 *
 * @param seBarrierBreak
 * @text Barrier Break SE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Raise1
 *
 * @param --- Random Game Settings ---
 *
 * @param randomMinWidth
 * @text Min Board Width
 * @parent --- Random Game Settings ---
 * @type number @min 10 @default 15
 *
 * @param randomMaxWidth
 * @text Max Board Width
 * @parent --- Random Game Settings ---
 * @type number @min 10 @default 30
 *
 * @param randomMinHeight
 * @text Min Board Height
 * @parent --- Random Game Settings ---
 * @type number @min 10 @default 15
 *
 * @param randomMaxHeight
 * @text Max Board Height
 * @parent --- Random Game Settings ---
 * @type number @min 10 @default 25
 *
 * @param randomMinSpeed
 * @text Min Starting Speed
 * @parent --- Random Game Settings ---
 * @desc Lower is faster.
 * @type number @decimals 2 @min 1 @default 6
 *
 * @param randomMaxSpeed
 * @text Max Starting Speed
 * @parent --- Random Game Settings ---
 * @desc Lower is faster.
 * @type number @decimals 2 @min 1 @default 1
 *
 * @command startSnake
 * @text Start Snake
 * @desc Shows the Snake minigame scene with specific settings.
 *
 * @arg enableOutline
 * @text Enable Board Outline
 * @desc If true, an outline will be drawn around the board.
 * @type boolean
 * @default true
 *
 * @arg outlineThickness
 * @text Outline Thickness
 * @desc The thickness of the board outline in pixels.
 * @type number
 * @min 1
 * @default 3
 *
 * @arg outlineColor
 * @text Outline Color
 * @desc The color of the outline in hex format (e.g., #CCCCCC).
 * @type string
 * @default #CCCCCC
 *
 * @arg outlineAlpha
 * @text Outline Opacity
 * @desc The transparency of the outline. 0.0 is invisible, 1.0 is opaque.
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0 
 *
 * @arg width
 * @text Board Width
 * @desc Width of the board in cells. Use 0 for default.
 * @type number @default 0
 *
 * @arg height
 * @text Board Height
 * @desc Height of the board in cells. Use 0 for default.
 * @type number @default 0
 *
 * @arg speed
 * @text Starting Speed
 * @desc Frames per move (lower is faster). Use 0 for default.
 * @type number @decimals 2 @default 0
 *
 * @arg scoreVarId
 * @text Score Variable ID
 * @desc Variable to store the final score. Use 0 for default.
 * @type variable @default 0
 *
 * @arg snakeColor
 * @text Snake Color
 * @desc Set snake color (e.g., #FF5733). Leave blank for default.
 * @type string @default
 *
 * @arg wrapAround
 * @text Wrap Around Board
 * @desc If true, the snake wraps around the edges instead of dying.
 * @type boolean @default false
 *
 * @param --- Text & UI Settings ---
 * @desc Customize the text displayed in the game and their positions.
 *
 * @param textScoreLabel
 * @parent --- Text & UI Settings ---
 * @text "Score" Label
 * @desc The text for the "Score" label.
 * @type string
 * @default Score: 
 *
 * @param textScoreOffsetX
 * @parent --- Text & UI Settings ---
 * @text "Score" Label X Offset
 * @desc Horizontal offset for the Score text.
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textScoreOffsetY
 * @parent --- Text & UI Settings ---
 * @text "Score" Label Y Offset
 * @desc Vertical offset for the Score text.
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textSpeedUp
 * @parent --- Text & UI Settings ---
 * @text "Speed Up" Text
 * @desc Text displayed when the snake speeds up.
 * @type string
 * @default SPEED UP!
 *
 * @param textSlowDown
 * @parent --- Text & UI Settings ---
 * @text "Slow Down" Text
 * @desc Text displayed when the snake slows down.
 * @type string
 * @default SPEED DOWN!
 *
 * @param textEffectOffsetX
 * @parent --- Text & UI Settings ---
 * @text Speed Effect X Offset
 * @desc Horizontal offset for the speed effect text.
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textEffectOffsetY
 * @parent --- Text & UI Settings ---
 * @text Speed Effect Y Offset
 * @desc Vertical offset for the speed effect text.
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textControlTitle
 * @parent --- Text & UI Settings ---
 * @text "Control Choice" Title
 * @desc Title on the control selection screen.
 * @type string
 * @default Choose Your Controls
 *
 * @param textControlButton1
 * @parent --- Text & UI Settings ---
 * @text "Keyboard" Button
 * @desc Text for the Keyboard control button.
 * @type string
 * @default Keyboard
 *
 * @param textControlButton2
 * @parent --- Text & UI Settings ---
 * @text "Mouse" Button
 * @desc Text for the Mouse control button.
 * @type string
 * @default Mouse
 *
 * @param textPaused
 * @parent --- Text & UI Settings ---
 * @text "Paused" Message
 * @desc Message shown when the game is paused.
 * @type string
 * @default PAUSED
 *
 * @param textPausedSub
 * @parent --- Text & UI Settings ---
 * @text "Paused Sub-message"
 * @desc Sub-message shown when the game is paused.
 * @type string
 * @default Ctrl: Exit | X/R-Click: Resume
 *
 * @param textGameOver
 * @parent --- Text & UI Settings ---
 * @text "Game Over" Message
 * @desc Message shown when the game ends.
 * @type string
 * @default GAME OVER
 *
 * @param textEndContinue
 * @parent --- Text & UI Settings ---
 * @text "Continue" Prompt
 * @desc The prompt to continue on the end screen.
 * @type string
 * @default Press any key to continue
 *
 * @param textCenterMessageOffsetX
 * @parent --- Text & UI Settings ---
 * @text Center Message X Offset
 * @desc Horizontal offset for all main center messages (Paused, Game Over).
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textCenterMessageOffsetY
 * @parent --- Text & UI Settings ---
 * @text Center Message Y Offset
 * @desc Vertical offset for all main center messages (Paused, Game Over).
 * @type number
 * @min -9999
 * @default 0
 *
 * @command startCustomRandomSnake
 * @text Start Random Snake
 * @desc Starts a game with custom randomized settings. If leave blank using in plugin parameter.
 *
 * @arg wrapAround
 * @text Wrap Around Board
 * @desc If true, the snake wraps around the edges instead of dying.
 * @type boolean
 * @default false
 *
 * @arg enableOutline
 * @text Enable Board Outline
 * @desc If true, an outline will be drawn around the board.
 * @type boolean
 * @default true
 *
 * @arg outlineThickness
 * @text Outline Thickness
 * @desc The thickness of the board outline in pixels.
 * @type number
 * @min 1
 * @default 3
 *
 * @arg outlineColor
 * @text Outline Color
 * @desc The color of the outline in hex format (e.g., #CCCCCC).
 * @type string
 * @default #CCCCCC
 *
 * @arg outlineAlpha
 * @text Outline Opacity
 * @desc The transparency of the outline. 0.0 is invisible, 1.0 is opaque.
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0
 *
 * @arg minWidth
 * @text Min Board Width
 * @desc Minimum board width for random generation.
 * @type number
 * @min 3
 * @default
 *
 * @arg maxWidth
 * @text Max Board Width
 * @desc Maximum board width for random generation.
 * @type number
 * @min 3
 * @default
 *
 * @arg minHeight
 * @text Min Board Height
 * @desc Minimum board height for random generation.
 * @type number
 * @min 3
 * @default 
 *
 * @arg maxHeight
 * @text Max Board Height
 * @desc Maximum board height for random generation.
 * @type number
 * @min 3
 * @default
 *
 * @arg minSpeed
 * @text Min Starting Speed
 * @desc Minimum starting speed (lower is faster).
 * @type number
 * @decimals 2
 * @min 1
 * @default
 *
 * @arg maxSpeed
 * @text Max Starting Speed
 * @desc Maximum starting speed (lower is faster).
 * @type number
 * @decimals 2
 * @min 10
 * @default
 *
 * @arg scoreVarId
 * @text Score Variable ID
 * @desc Variable to store the final score. Use 0 for default.
 * @type variable
 * @default
 *
 * @arg snakeColor
 * @text Snake Color
 * @desc Set snake color (e.g., #FF5733). Leave blank for default.
 * @type string
 * @default
 */

/*:ja
 * @target MZ
 * @plugindesc 色のカスタマイズや特殊なレベルがある、ダイナミックなスネークミニゲームです。
 * @author Ignideus777
 * @version 2.3.0
 *
 * @help
 * - RPGツクールMZ用のスネークミニゲームです。
 * - PIXIグラフィックスのみを使用しています。
 * - 画像ファイルは不要です。
 * - マウスとキーボード操作に対応しています。
 * - デフォルト設定のままでも簡単にセットアップできます。
 * - ゲームごとに設定をランダム化できます。
 *
 * --- 操作とゲームプレイ ---
 * キーボード: 矢印キーでスネークを操作します。
 * マウス: カーソルを動かしてスネークを操作します。
 * ポーズ: 「Ctrl」キーで一時停止します。
 * 再開: ポーズ中に「X」キー、「ESC」キー、または右クリックで再開します。
 * ポーズ中に終了: ポーズ中に再度「Ctrl」キーを押すと終了します。
 *
 * --- 表示 ---
 * 「背景画像ファイル」パラメータが空の場合、背景は透明になり、
 * 下のマップやシーンが表示されます。
 *
 * --- サウンドエフェクト ---
 * すべての効果音はプラグインパラメータでカスタマイズできます。
 * サウンドが空欄の場合、デフォルトのエンジンサウンドが使用されます。
 * 
 * @param --- Controls ---
 * @text --- 操作設定 ---
 *
 * @param enableMouseSupport
 * @text マウスサポートを有効化
 * @parent --- Controls ---
 * @desc trueの場合、選択画面で「マウス」オプションが利用可能になります。
 * @type boolean
 * @default true
 *
 * @param --- General Settings ---
 * @text --- 基本設定 ---
 *
 * @param startSpeed
 * @text デフォルトの開始速度
 * @parent --- General Settings ---
 * @desc スネークが1マス移動するまでのフレーム数。数値が小さいほど速くなります。
 * @type number
 * @decimals 2
 * @default 6
 *
 * @param scoreVariableId
 * @text デフォルトのスコア格納変数ID
 * @parent --- General Settings ---
 * @type variable
 * @default 1
 *
 * @param boardWidth
 * @text デフォルトのボードの幅
 * @parent --- General Settings ---
 * @type number
 * @default 25
 *
 * @param boardHeight
 * @text デフォルトのボードの高さ
 * @parent --- General Settings ---
 * @type number
 * @default 20
 *
 * @param --- Display Settings ---
 * @text --- 表示設定 ---
 *
 * @param defaultSnakeColor
 * @text デフォルトのスネークの色
 * @parent --- Display Settings ---
 * @desc スネークのデフォルト色を16進数カラーコードで指定します (例: #E1C16E)。
 * @type string
 * @default #E1C16E
 *
 * @param backImage
 * @text 背景画像ファイル
 * @parent --- Display Settings ---
 * @desc オプションで pictures フォルダから背景画像を指定できます。画面いっぱいに自動で引き伸ばされます。png形式のファイルである必要があります。
 * @type file
 * @dir img/pictures/
 * @default
 *
 * @param --- Board Outline Settings ---
 * @text --- ゲーム盤の枠線設定 ---
 *
 * @param enableOutline
 * @text ゲーム盤の枠線を表示
 * @parent --- Board Outline Settings ---
 * @desc trueの場合、ゲーム盤の周りに枠線が描画されます。
 * @type boolean
 * @default true
 *
 * @param outlineThickness
 * @text 枠線の太さ
 * @parent --- Board Outline Settings ---
 * @desc ゲーム盤の枠線の太さをピクセル単位で指定します。
 * @type number
 * @min 1
 * @default 3
 *
 * @param outlineColor
 * @text 枠線の色
 * @parent --- Board Outline Settings ---
 * @desc 枠線の色を16進数カラーコードで指定します (例: #CCCCCC)。
 * @type string
 * @default #CCCCCC
 *
 * @param outlineAlpha
 * @text 枠線の不透明度
 * @parent --- Board Outline Settings ---
 * @desc 枠線の不透明度です。0.0で透明、1.0で不透明になります。
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0
 *
 * @param --- Gameplay Settings ---
 * @text --- ゲームプレイ設定 ---
 *
 * @param speedIncrement
 * @text 速度上昇値
 * @parent --- Gameplay Settings ---
 * @desc 茶色のエサを食べた後、移動タイマーがどれだけ減少するかを指定します。
 * @type number
 * @decimals 2
 * @default 0.15
 *
 * @param speedEffectDuration
 * @text 速度効果の持続時間
 * @parent --- Gameplay Settings ---
 * @desc スピードアップ/ダウン効果が持続する時間(フレーム単位)。60 = 1秒。
 * @type number
 * @default 300
 *
 * @param speedUpFactor
 * @text スピードアップ倍率
 * @parent --- Gameplay Settings ---
 * @desc 移動遅延をこの値で割ります。例: 2を指定するとスネークが2倍速くなります。
 * @type number
 * @decimals 2
 * @min 1
 * @default 1.5
 *
 * @param slowDownFactor
 * @text スピードダウン倍率
 * @parent --- Gameplay Settings ---
 * @desc 移動遅延をこの値で割ります。例: 0.5を指定するとスネークが2倍遅くなります。
 * @type number
 * @decimals 2
 * @min 0.01
 * @max 1
 * @default 0.35
 *
 * @param dietShrinkAmount
 * @text ダイエット効果量
 * @parent --- Gameplay Settings ---
 * @desc ダイエット(ピンク色)のエサで減少する節の数です。
 * @type number
 * @min 1
 * @default 3
 *
 * @param superFoodScore
 * @text スーパーフードのスコア
 * @parent --- Gameplay Settings ---
 * @desc スーパー(青緑色)のエサを食べた時のスコアです。
 * @type number @min 0 @default 5
 *
 * @param superFoodGrowth
 * @text スーパーフードの成長量
 * @parent --- Gameplay Settings ---
 * @desc スーパーフードでスネークが成長する節の数です。
 * @type number @min 1 @default 5
 *
 * @param speedUpFoodScore
 * @text スピードアップフードのスコア
 * @parent --- Gameplay Settings ---
 * @desc スピードアップ(赤色)のエサを食べた時のスコアです。スネークも1節成長します。
 * @type number @min 0 @default 2
 *
 * @param slowDownFoodScore
 * @text スピードダウンフードのスコア
 * @parent --- Gameplay Settings ---
 * @desc スピードダウン(緑色)のエサを食べた時のスコアです。スネークも1節成長します。
 * @type number @min 0 @default 2
 *
 * @param dietFoodScore
 * @text ダイエットフードのスコア
 * @parent --- Gameplay Settings ---
 * @desc スネークを縮小させるダイエット(ピンク色)のエサを食べた時のスコアです。
 * @type number @min 0 @default 1
 *
 * @param barrierFoodScore
 * @text バリアフードのスコア
 * @parent --- Gameplay Settings ---
 * @desc バリア(白色)のエサを食べた時のスコアです。
 * @type number @min 0 @default 1
 *
 * @param --- Food Spawn Chance (%) ---
 * @text --- エサの出現率 (%) ---
 *
 * @param redFoodChance
 * @text 赤色のエサ (スピードアップ)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 20
 *
 * @param greenFoodChance
 * @text 緑色のエサ (スピードダウン)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 20
 *
 * @param purpleFoodChance
 * @text 青緑色のエサ (スーパー)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 10
 *
 * @param blackFoodChance
 * @text 黒色のエサ (毒)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 15
 *
 * @param whiteFoodChance
 * @text 白色のエサ (バリア)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 12
 *
 * @param pinkFoodChance
 * @text ピンク色のエサ (ダイエット)
 * @parent --- Food Spawn Chance (%) ---
 * @type number @min 0 @max 100 @default 12
 *
 * @param --- Food Lifespan Settings ---
 * @text --- エサの寿命設定 ---
 * @desc 特殊なエサが消えるまでに、通常(茶色)のエサをいくつ食べる必要があるかを設定します。0に設定すると永続します。
 *
 * @param speedUpFoodLifespan
 * @text スピードアップフードの寿命
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 0
 *
 * @param slowDownFoodLifespan
 * @text スピードダウンフードの寿命
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 0
 *
 * @param superFoodLifespan
 * @text スーパーフードの寿命
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 2
 *
 * @param poisonFoodLifespan
 * @text 毒フードの寿命
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 3
 *
 * @param barrierFoodLifespan
 * @text バリアフードの寿命
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 5
 *
 * @param dietFoodLifespan
 * @text ダイエットフードの寿命
 * @parent --- Food Lifespan Settings ---
 * @type number @min 0 @default 5
 *
 * @param --- Sound Effects ---
 * @text --- サウンドエフェクト ---
 * @desc オプションのカスタム効果音です。空欄の場合、デフォルトのサウンドが使用されます。
 *
 * @param seConfirm
 * @text 決定SE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Decision2
 *
 * @param seCursor
 * @text カーソルSE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Cursor3
 *
 * @param seEat
 * @text エサを食べるSE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Heal1
 *
 * @param seGameOver
 * @text ゲームオーバーSE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Buzzer1
 *
 * @param seBarrierBreak
 * @text バリア破壊SE
 * @parent --- Sound Effects ---
 * @type file @dir audio/se/ @default Raise1
 *
 * @param --- Text & UI Settings ---
 * @text --- テキストとUI設定 ---
 * @desc ゲーム内に表示されるテキストとその位置をカスタマイズします。
 *
 * @param textScoreLabel
 * @parent --- Text & UI Settings ---
 * @text 「スコア」ラベル
 * @desc 「スコア」のラベルテキスト。
 * @type string
 * @default スコア: 
 *
 * @param textScoreOffsetX
 * @parent --- Text & UI Settings ---
 * @text 「スコア」Xオフセット
 * @desc スコアテキストの水平オフセット。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textScoreOffsetY
 * @parent --- Text & UI Settings ---
 * @text 「スコア」Yオフセット
 * @desc スコアテキストの垂直オフセット。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textSpeedUp
 * @parent --- Text & UI Settings ---
 * @text 「スピードアップ」テキスト
 * @desc スネークがスピードアップしたときに表示されるテキスト。
 * @type string
 * @default スピードアップ！
 *
 * @param textSlowDown
 * @parent --- Text & UI Settings ---
 * @text 「スピードダウン」テキスト
 * @desc スネークがスピードダウンしたときに表示されるテキスト。
 * @type string
 * @default スピードダウン！
 *
 * @param textEffectOffsetX
 * @parent --- Text & UI Settings ---
 * @text 速度効果Xオフセット
 * @desc 速度効果テキストの水平オフセット。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textEffectOffsetY
 * @parent --- Text & UI Settings ---
 * @text 速度効果Yオフセット
 * @desc 速度効果テキストの垂直オフセット。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textControlTitle
 * @parent --- Text & UI Settings ---
 * @text 「操作選択」タイトル
 * @desc 操作選択画面のタイトル。
 * @type string
 * @default 操作方法を選択
 *
 * @param textControlButton1
 * @parent --- Text & UI Settings ---
 * @text 「キーボード」ボタン
 * @desc キーボード操作ボタンのテキスト。
 * @type string
 * @default キーボード
 *
 * @param textControlButton2
 * @parent --- Text & UI Settings ---
 * @text 「マウス」ボタン
 * @desc マウス操作ボタンのテキスト。
 * @type string
 * @default マウス
 *
 * @param textPaused
 * @parent --- Text & UI Settings ---
 * @text 「ポーズ」メッセージ
 * @desc ゲームが一時停止したときに表示されるメッセージ。
 * @type string
 * @default ポーズ
 *
 * @param textPausedSub
 * @parent --- Text & UI Settings ---
 * @text 「ポーズ中」サブメッセージ
 * @desc ゲームが一時停止したときに表示されるサブメッセージ。
 * @type string
 * @default Ctrl: 終了 | X/右クリック: 再開
 *
 * @param textGameOver
 * @parent --- Text & UI Settings ---
 * @text 「ゲームオーバー」メッセージ
 * @desc ゲーム終了時に表示されるメッセージ。
 * @type string
 * @default ゲームオーバー
 *
 * @param textEndContinue
 * @parent --- Text & UI Settings ---
 * @text 「続行」プロンプト
 * @desc 終了画面の続行プロンプト。
 * @type string
 * @default 何かキーを押して続行
 *
 * @param textCenterMessageOffsetX
 * @parent --- Text & UI Settings ---
 * @text 中央メッセージXオフセット
 * @desc 全ての中央メッセージ(ポーズ, ゲームオーバー)の水平オフセット。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textCenterMessageOffsetY
 * @parent --- Text & UI Settings ---
 * @text 中央メッセージYオフセット
 * @desc 全ての中央メッセージ(ポーズ, ゲームオーバー)の垂直オフセット。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param --- Random Game Settings ---
 * @text --- ランダムゲーム設定 ---
 *
 * @param randomMinWidth
 * @text 最小ボード幅
 * @parent --- Random Game Settings ---
 * @type number @min 10 @default 15
 *
 * @param randomMaxWidth
 * @text 最大ボード幅
 * @parent --- Random Game Settings ---
 * @type number @min 10 @default 30
 *
 * @param randomMinHeight
 * @text 最小ボード高さ
 * @parent --- Random Game Settings ---
 * @type number @min 10 @default 15
 *
 * @param randomMaxHeight
 * @text 最大ボード高さ
 * @parent --- Random Game Settings ---
 * @type number @min 10 @default 25
 *
 * @param randomMinSpeed
 * @text 最小開始速度
 * @parent --- Random Game Settings ---
 * @desc 数値が小さいほど速くなります。
 * @type number @decimals 2 @min 1 @default 6
 *
 * @param randomMaxSpeed
 * @text 最大開始速度
 * @parent --- Random Game Settings ---
 * @desc 数値が小さいほど速くなります。
 * @type number @decimals 2 @min 1 @default 1
 *
 * @command startSnake
 * @text スネーク開始
 * @desc 指定した設定でスネークミニゲームのシーンを開始します。
 *
 * @arg enableOutline
 * @text ゲーム盤の枠線を表示
 * @desc trueの場合、ゲーム盤の周りに枠線が描画されます。
 * @type boolean
 * @default true
 *
 * @arg outlineThickness
 * @text 枠線の太さ
 * @desc ゲーム盤の枠線の太さをピクセル単位で指定します。
 * @type number
 * @min 1
 * @default 3
 *
 * @arg outlineColor
 * @text 枠線の色
 * @desc 枠線の色を16進数カラーコードで指定します (例: #CCCCCC)。
 * @type string
 * @default #CCCCCC
 *
 * @arg outlineAlpha
 * @text 枠線の不透明度
 * @desc 枠線の不透明度です。0.0で透明、1.0で不透明になります。
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0 
 *
 * @arg width
 * @text ボードの幅
 * @desc ボードの幅をマス単位で指定します。0でデフォルト値を使用。
 * @type number @default 0
 *
 * @arg height
 * @text ボードの高さ
 * @desc ボードの高さをマス単位で指定します。0でデフォルト値を使用。
 * @type number @default 0
 *
 * @arg speed
 * @text 開始速度
 * @desc 1マス移動するまでのフレーム数(数値が小さいほど高速)。0でデフォルト値を使用。
 * @type number @decimals 2 @default 0
 *
 * @arg scoreVarId
 * @text スコアを格納する変数ID
 * @desc 最終スコアを格納する変数です。0でデフォルト値を使用。
 * @type variable @default 0
 *
 * @arg snakeColor
 * @text スネークの色
 * @desc スネークの色を指定します (例: #FF5733)。空欄でデフォルト値を使用。
 * @type string @default
 *
 * @arg wrapAround
 * @text 画面端でループ
 * @desc trueの場合、スネークは壁に衝突せず反対側から出現します。
 * @type boolean @default false
 *
 * 
 * @command startCustomRandomSnake
 * @text ランダムスネーク開始
 * @desc カスタムのランダム設定でゲームを開始します。空欄の場合はプラグインパラメータの値を使用します。
 *
 * @arg wrapAround
 * @text 画面端でループ
 * @desc trueの場合、スネークは壁に衝突せず反対側から出現します。
 * @type boolean
 * @default false
 *
 * @arg enableOutline
 * @text ゲーム盤の枠線を表示
 * @desc trueの場合、ゲーム盤の周りに枠線が描画されます。
 * @type boolean
 * @default true
 *
 * @arg outlineThickness
 * @text 枠線の太さ
 * @desc ゲーム盤の枠線の太さをピクセル単位で指定します。
 * @type number
 * @min 1
 * @default 3
 *
 * @arg outlineColor
 * @text 枠線の色
 * @desc 枠線の色を16進数カラーコードで指定します (例: #CCCCCC)。
 * @type string
 * @default #CCCCCC
 *
 * @arg outlineAlpha
 * @text 枠線の不透明度
 * @desc 枠線の不透明度です。0.0で透明、1.0で不透明になります。
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0
 *
 * @arg minWidth
 * @text 最小ボード幅
 * @desc ランダム生成時の最小ボード幅です。
 * @type number
 * @min 3
 * @default
 *
 * @arg maxWidth
 * @text 最大ボード幅
 * @desc ランダム生成時の最大ボード幅です。
 * @type number
 * @min 3
 * @default
 *
 * @arg minHeight
 * @text 最小ボード高さ
 * @desc ランダム生成時の最小ボード高さです。
 * @type number
 * @min 3
 * @default 
 *
 * @arg maxHeight
 * @text 最大ボード高さ
 * @desc ランダム生成時の最大ボード高さです。
 * @type number
 * @min 3
 * @default
 *
 * @arg minSpeed
 * @text 最小開始速度
 * @desc 最小開始速度です(数値が小さいほど高速)。
 * @type number
 * @decimals 2
 * @min 1
 * @default
 *
 * @arg maxSpeed
 * @text 最大開始速度
 * @desc 最大開始速度です(数値が小さいほど高速)。
 * @type number
 * @decimals 2
 * @min 10
 * @default
 *
 * @arg scoreVarId
 * @text スコアを格納する変数ID
 * @desc 最終スコアを格納する変数です。0でデフォルト値を使用。
 * @type variable
 * @default
 *
 * @arg snakeColor
 * @text スネークの色
 * @desc スネークの色を指定します (例: #FF5733)。空欄でデフォルト値を使用。
 * @type string
 * @default
 */

/*:zh_CN
 * @target MZ
 * @plugindesc 一款动态的贪吃蛇小游戏，支持颜色定制和特殊关卡。
 * @author Ignideus777
 * @version 2.3.0
 *
 * @help
 * - RPG Maker MZ 的贪吃蛇小游戏。
 * - 仅使用 PIXI 图形渲染。
 * - 无需任何图像文件。
 * - 支持鼠标和键盘操作。
 * - 可保留默认设置以轻松启动。
 * - 每次小游戏都可以是随机的。
 *
 * --- 操作与玩法 ---
 * 键盘: 使用方向键控制蛇的移动方向。
 * 鼠标: 移动光标来引导蛇的方向。
 * 暂停: 按 'ctrl' 键暂停游戏。
 * 继续: 暂停时，按 'X'、'ESC' 或鼠标右键继续。
 * 退出暂停的游戏: 暂停时，再次按 'ctrl' 键退出。
 *
 * --- 显示 ---
 * 如果“背景文件”参数为空，背景将是透明的，会显示下方的地图或场景。
 *
 * --- 音效 ---
 * 您可以在插件参数中自定义所有音效。如果留空，将使用引擎的默认音效。
 * 
 * @param --- 操作 ---
 *
 * @param enableMouseSupport
 * @text 启用鼠标支持
 * @parent --- 操作 ---
 * @desc 如果为 true，选择界面将出现“鼠标”选项。
 * @type boolean
 * @default true
 *
 * @param --- 通用设置 ---
 *
 * @param startSpeed
 * @text 默认初始速度
 * @parent --- 通用设置 ---
 * @desc 蛇每移动一格所需的帧数。数值越低，速度越快。
 * @type number
 * @decimals 2
 * @default 6
 *
 * @param scoreVariableId
 * @text 默认分数变量ID
 * @parent --- 通用设置 ---
 * @type variable
 * @default 1
 *
 * @param boardWidth
 * @text 默认棋盘宽度
 * @parent --- 通用设置 ---
 * @type number
 * @default 25
 *
 * @param boardHeight
 * @text 默认棋盘高度
 * @parent --- 通用设置 ---
 * @type number
 * @default 20
 *
 * @param --- 显示设置 ---
 *
 * @param defaultSnakeColor
 * @text 默认蛇的颜色
 * @parent --- 显示设置 ---
 * @desc 蛇的默认颜色，使用十六进制格式（例如：#E1C16E）。
 * @type string
 * @default #E1C16E
 *
 * @param backImage
 * @text 背景文件
 * @parent --- 显示设置 ---
 * @desc 可选的背景图片，从 pictures 文件夹中选择。会自动拉伸以填充屏幕。必须是png格式。
 * @type file
 * @dir img/pictures/
 * @default
 *
 * @param --- 棋盘边框设置 ---
 *
 * @param enableOutline
 * @text 启用棋盘边框
 * @parent --- 棋盘边框设置 ---
 * @desc 如果为 true，将在棋盘周围绘制边框。
 * @type boolean
 * @default true
 *
 * @param outlineThickness
 * @text 边框粗细
 * @parent --- 棋盘边框设置 ---
 * @desc 边框的厚度（单位：像素）。
 * @type number
 * @min 1
 * @default 3
 *
 * @param outlineColor
 * @text 边框颜色
 * @parent --- 棋盘边框设置 ---
 * @desc 边框的颜色，使用十六进制格式（例如：#CCCCCC）。
 * @type string
 * @default #CCCCCC
 *
 * @param outlineAlpha
 * @text 边框不透明度
 * @parent --- 棋盘边框设置 ---
 * @desc 边框的透明度。0.0为完全透明，1.0为完全不透明。
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0
 *
 * @param --- 玩法设置 ---
 *
 * @param speedIncrement
 * @text 速度增量
 * @parent --- 玩法设置 ---
 * @desc 吃掉一个棕色食物后，移动计时器减少的量。
 * @type number
 * @decimals 2
 * @default 0.15
 *
 * @param speedEffectDuration
 * @text 速度效果持续时间
 * @parent --- 玩法设置 ---
 * @desc 加速/减速效果的持续时间（帧）。60 = 1秒。
 * @type number
 * @default 300
 *
 * @param speedUpFactor
 * @text 加速因子
 * @parent --- 玩法设置 ---
 * @desc 用于除以速度延迟。例如，2会使蛇快2倍。
 * @type number
 * @decimals 2
 * @min 1
 * @default 1.5
 *
 * @param slowDownFactor
 * @text 减速因子
 * @parent --- 玩法设置 ---
 * @desc 用于除以速度延迟。例如，0.5会使蛇慢2倍。
 * @type number
 * @decimals 2
 * @min 0.01
 * @max 1
 * @default 0.35
 *
 * @param dietShrinkAmount
 * @text 减肥食物缩短量
 * @parent --- 玩法设置 ---
 * @desc 减肥（粉色）食物移除的蛇段数量。
 * @type number
 * @min 1
 * @default 3
 *
 * @param superFoodScore
 * @text 超级食物分数
 * @parent --- 玩法设置 ---
 * @desc 吃掉超级（青色）食物获得的分数。
 * @type number @min 0 @default 5
 *
 * @param superFoodGrowth
 * @text 超级食物尾部增长
 * @parent --- 玩法设置 ---
 * @desc 吃掉一个超级食物后蛇增长的段数。
 * @type number @min 1 @default 5
 *
 * @param speedUpFoodScore
 * @text 加速食物分数
 * @parent --- 玩法设置 ---
 * @desc 吃掉加速（红色）食物获得的分数。蛇也会增长1段。
 * @type number @min 0 @default 2
 *
 * @param slowDownFoodScore
 * @text 减速食物分数
 * @parent --- 玩法设置 ---
 * @desc 吃掉减速（绿色）食物获得的分数。蛇也会增长1段。
 * @type number @min 0 @default 2
 *
 * @param dietFoodScore
 * @text 减肥食物分数
 * @parent --- 玩法设置 ---
 * @desc 吃掉减肥（粉色）食物获得的分数，这会使蛇变短。
 * @type number @min 0 @default 1
 *
 * @param barrierFoodScore
 * @text 障碍食物分数
 * @parent --- 玩法设置 ---
 * @desc 吃掉障碍（白色）食物获得的分数。
 * @type number @min 0 @default 1
 *
 * @param --- 食物生成概率 (%) ---
 *
 * @param redFoodChance
 * @text 红色食物 (加速)
 * @parent --- 食物生成概率 (%) ---
 * @type number @min 0 @max 100 @default 20
 *
 * @param greenFoodChance
 * @text 绿色食物 (减速)
 * @parent --- 食物生成概率 (%) ---
 * @type number @min 0 @max 100 @default 20
 *
 * @param purpleFoodChance
 * @text 青色食物 (超级)
 * @parent --- 食物生成概率 (%) ---
 * @type number @min 0 @max 100 @default 10
 *
 * @param blackFoodChance
 * @text 黑色食物 (毒药)
 * @parent --- 食物生成概率 (%) ---
 * @type number @min 0 @max 100 @default 15
 *
 * @param whiteFoodChance
 * @text 白色食物 (障碍)
 * @parent --- 食物生成概率 (%) ---
 * @type number @min 0 @max 100 @default 12
 *
 * @param pinkFoodChance
 * @text 粉色食物 (减肥)
 * @parent --- 食物生成概率 (%) ---
 * @type number @min 0 @max 100 @default 12
 *
 * @param --- 食物持续时间设置 ---
 * @desc 设置在特殊食物消失前必须吃掉多少个主要（棕色）食物。设为0则永久存在。
 *
 * @param speedUpFoodLifespan
 * @text 加速食物持续时间
 * @parent --- 食物持续时间设置 ---
 * @type number @min 0 @default 0
 *
 * @param slowDownFoodLifespan
 * @text 减速食物持续时间
 * @parent --- 食物持续时间设置 ---
 * @type number @min 0 @default 0
 *
 * @param superFoodLifespan
 * @text 超级食物持续时间
 * @parent --- 食物持续时间设置 ---
 * @type number @min 0 @default 2
 *
 * @param poisonFoodLifespan
 * @text 毒药食物持续时间
 * @parent --- 食物持续时间设置 ---
 * @type number @min 0 @default 3
 *
 * @param barrierFoodLifespan
 * @text 障碍食物持续时间
 * @parent --- 食物持续时间设置 ---
 * @type number @min 0 @default 5
 *
 * @param dietFoodLifespan
 * @text 减肥食物持续时间
 * @parent --- 食物持续时间设置 ---
 * @type number @min 0 @default 5
 *
 * @param --- 音效 ---
 * @desc 可选的自定义音效。如果留空，将使用默认音效。
 *
 * @param seConfirm
 * @text 确认 音效
 * @parent --- 音效 ---
 * @type file @dir audio/se/ @default Decision2
 *
 * @param seCursor
 * @text 光标 音效
 * @parent --- 音效 ---
 * @type file @dir audio/se/ @default Cursor3
 *
 * @param seEat
 * @text 吃食物 音效
 * @parent --- 音效 ---
 * @type file @dir audio/se/ @default Heal1
 *
 * @param seGameOver
 * @text 游戏结束 音效
 * @parent --- 音效 ---
 * @type file @dir audio/se/ @default Buzzer1
 *
 * @param seBarrierBreak
 * @text 障碍破坏 音效
 * @parent --- 音效 ---
 * @type file @dir audio/se/ @default Raise1
 *
 * @param --- 文本与UI设置 ---
 * @desc 自定义游戏中显示的文本及其位置。
 *
 * @param textScoreLabel
 * @parent --- 文本与UI设置 ---
 * @text “分数”标签
 * @desc “分数”标签的文本。
 * @type string
 * @default 分数: 
 *
 * @param textScoreOffsetX
 * @parent --- 文本与UI设置 ---
 * @text “分数”标签 X 偏移
 * @desc 分数文本的水平偏移量。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textScoreOffsetY
 * @parent --- 文本与UI设置 ---
 * @text “分数”标签 Y 偏移
 * @desc 分数文本的垂直偏移量。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textSpeedUp
 * @parent --- 文本与UI设置 ---
 * @text “加速”文本
 * @desc 蛇加速时显示的文本。
 * @type string
 * @default 加速！
 *
 * @param textSlowDown
 * @parent --- 文本与UI设置 ---
 * @text “减速”文本
 * @desc 蛇减速时显示的文本。
 * @type string
 * @default 减速！
 *
 * @param textEffectOffsetX
 * @parent --- 文本与UI设置 ---
 * @text 速度效果 X 偏移
 * @desc 速度效果文本的水平偏移量。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textEffectOffsetY
 * @parent --- 文本与UI设置 ---
 * @text 速度效果 Y 偏移
 * @desc 速度效果文本的垂直偏移量。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textControlTitle
 * @parent --- 文本与UI设置 ---
 * @text “选择操作”标题
 * @desc 操作选择界面的标题。
 * @type string
 * @default 选择您的操作方式
 *
 * @param textControlButton1
 * @parent --- 文本与UI设置 ---
 * @text “键盘”按钮
 * @desc 键盘操作按钮的文本。
 * @type string
 * @default 键盘
 *
 * @param textControlButton2
 * @parent --- 文本与UI设置 ---
 * @text “鼠标”按钮
 * @desc 鼠标操作按钮的文本。
 * @type string
 * @default 鼠标
 *
 * @param textPaused
 * @parent --- 文本与UI设置 ---
 * @text “已暂停”信息
 * @desc 游戏暂停时显示的信息。
 * @type string
 * @default 已暂停
 *
 * @param textPausedSub
 * @parent --- 文本与UI设置 ---
 * @text “已暂停”副信息
 * @desc 游戏暂停时显示的副信息。
 * @type string
 * @default Ctrl: 退出 | X/鼠标右键: 继续
 *
 * @param textGameOver
 * @parent --- 文本与UI设置 ---
 * @text “游戏结束”信息
 * @desc 游戏结束时显示的信息。
 * @type string
 * @default 游戏结束
 *
 * @param textEndContinue
 * @parent --- 文本与UI设置 ---
 * @text “继续”提示
 * @desc 结束画面上的继续提示。
 * @type string
 * @default 按任意键继续
 *
 * @param textCenterMessageOffsetX
 * @parent --- 文本与UI设置 ---
 * @text 中心信息 X 偏移
 * @desc 所有主要中心信息（暂停、游戏结束）的水平偏移量。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param textCenterMessageOffsetY
 * @parent --- 文本与UI设置 ---
 * @text 中心信息 Y 偏移
 * @desc 所有主要中心信息（暂停、游戏结束）的垂直偏移量。
 * @type number
 * @min -9999
 * @default 0
 *
 * @param --- 随机游戏设置 ---
 *
 * @param randomMinWidth
 * @text 最小棋盘宽度
 * @parent --- 随机游戏设置 ---
 * @type number @min 10 @default 15
 *
 * @param randomMaxWidth
 * @text 最大棋盘宽度
 * @parent --- 随机游戏设置 ---
 * @type number @min 10 @default 30
 *
 * @param randomMinHeight
 * @text 最小棋盘高度
 * @parent --- 随机游戏设置 ---
 * @type number @min 10 @default 15
 *
 * @param randomMaxHeight
 * @text 最大棋盘高度
 * @parent --- 随机游戏设置 ---
 * @type number @min 10 @default 25
 *
 * @param randomMinSpeed
 * @text 最小初始速度
 * @parent --- 随机游戏设置 ---
 * @desc 数值越低，速度越快。
 * @type number @decimals 2 @min 1 @default 6
 *
 * @param randomMaxSpeed
 * @text 最大初始速度
 * @parent --- 随机游戏设置 ---
 * @desc 数值越低，速度越快。
 * @type number @decimals 2 @min 1 @default 1
 *
 * @command startSnake
 * @text 开始贪吃蛇
 * @desc 使用特定设置显示贪吃蛇小游戏场景。
 *
 * @arg enableOutline
 * @text 启用棋盘边框
 * @desc 如果为 true，将在棋盘周围绘制边框。
 * @type boolean
 * @default true
 *
 * @arg outlineThickness
 * @text 边框粗细
 * @desc 边框的厚度（单位：像素）。
 * @type number
 * @min 1
 * @default 3
 *
 * @arg outlineColor
 * @text 边框颜色
 * @desc 边框的颜色，使用十六进制格式（例如：#CCCCCC）。
 * @type string
 * @default #CCCCCC
 *
 * @arg outlineAlpha
 * @text 边框不透明度
 * @desc 边框的透明度。0.0为完全透明，1.0为完全不透明。
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0 
 *
 * @arg width
 * @text 棋盘宽度
 * @desc 棋盘的宽度（单位：格子）。使用0则为默认值。
 * @type number @default 0
 *
 * @arg height
 * @text 棋盘高度
 * @desc 棋盘的高度（单位：格子）。使用0则为默认值。
 * @type number @default 0
 *
 * @arg speed
 * @text 初始速度
 * @desc 每移动一格的帧数（数值越低越快）。使用0则为默认值。
 * @type number @decimals 2 @default 0
 *
 * @arg scoreVarId
 * @text 分数变量ID
 * @desc 用于存储最终分数的变量。使用0则为默认值。
 * @type variable @default 0
 *
 * @arg snakeColor
 * @text 蛇的颜色
 * @desc 设置蛇的颜色（例如：#FF5733）。留空则为默认值。
 * @type string @default
 *
 * @arg wrapAround
 * @text 穿墙模式
 * @desc 如果为 true，蛇会从边缘穿过而不是死亡。
 * @type boolean @default false
 *
 * 
 * @command startCustomRandomSnake
 * @text 开始随机贪吃蛇
 * @desc 使用自定义的随机设置开始游戏。如果留空则使用插件参数中的设置。
 *
 * @arg wrapAround
 * @text 穿墙模式
 * @desc 如果为 true，蛇会从边缘穿过而不是死亡。
 * @type boolean
 * @default false
 *
 * @arg enableOutline
 * @text 启用棋盘边框
 * @desc 如果为 true，将在棋盘周围绘制边框。
 * @type boolean
 * @default true
 *
 * @arg outlineThickness
 * @text 边框粗细
 * @desc 边框的厚度（单位：像素）。
 * @type number
 * @min 1
 * @default 3
 *
 * @arg outlineColor
 * @text 边框颜色
 * @desc 边框的颜色，使用十六进制格式（例如：#CCCCCC）。
 * @type string
 * @default #CCCCCC
 *
 * @arg outlineAlpha
 * @text 边框不透明度
 * @desc 边框的透明度。0.0为完全透明，1.0为完全不透明。
 * @type number
 * @decimals 2
 * @min 0
 * @max 1
 * @default 1.0
 *
 * @arg minWidth
 * @text 最小棋盘宽度
 * @desc 随机生成的最小棋盘宽度。
 * @type number
 * @min 3
 * @default
 *
 * @arg maxWidth
 * @text 最大棋盘宽度
 * @desc 随机生成的最大棋盘宽度。
 * @type number
 * @min 3
 * @default
 *
 * @arg minHeight
 * @text 最小棋盘高度
 * @desc 随机生成的最小棋盘高度。
 * @type number
 * @min 3
 * @default 
 *
 * @arg maxHeight
 * @text 最大棋盘高度
 * @desc 随机生成的最大棋盘高度。
 * @type number
 * @min 3
 * @default
 *
 * @arg minSpeed
 * @text 最小初始速度
 * @desc 最小初始速度（数值越低越快）。
 * @type number
 * @decimals 2
 * @min 1
 * @default
 *
 * @arg maxSpeed
 * @text 最大初始速度
 * @desc 最大初始速度（数值越低越快）。
 * @type number
 * @decimals 2
 * @min 10
 * @default
 *
 * @arg scoreVarId
 * @text 分数变量ID
 * @desc 用于存储最终分数的变量。使用0则为默认值。
 * @type variable
 * @default
 *
 * @arg snakeColor
 * @text 蛇的颜色
 * @desc 设置蛇的颜色（例如：#FF5733）。留空则为默认值。
 * @type string
 * @default
 */
 

(() => {
    'use strict';

    const pluginName = "Ignideus777_Snake";
    const parameters = PluginManager.parameters(pluginName);

    const parseLocalizableText = (paramString, fallback) => {
        const str = paramString || fallback;
        if (!str || !str.includes('#{')) {
            return str;
        }
        const locale = ($gameSystem ? $gameSystem.locale() : navigator.language).slice(0, 2);
        const entries = str.split('|');
        const langMap = new Map();
        let defaultText = '';

        for (const entry of entries) {
            const match = entry.match(/^(\w{2})#\{(.+?)\}$/);
            if (match) {
                const lang = match[1];
                const text = match[2];
                langMap.set(lang, text);
                if (!defaultText) defaultText = text;
            } else if (!defaultText) {
                defaultText = entry;
            }
        }
        return langMap.get(locale) || defaultText || str;
    };

    const uiTexts = {
        scoreLabel: parseLocalizableText(parameters.textScoreLabel, 'Score: '),
        scoreOffsetX: Number(parameters.textScoreOffsetX || 0),
        scoreOffsetY: Number(parameters.textScoreOffsetY || 0),
        speedUp: parseLocalizableText(parameters.textSpeedUp, 'SPEED UP!'),
        slowDown: parseLocalizableText(parameters.textSlowDown, 'SPEED DOWN!'),
        effectOffsetX: Number(parameters.textEffectOffsetX || 0),
        effectOffsetY: Number(parameters.textEffectOffsetY || 0),
        controlTitle: parseLocalizableText(parameters.textControlTitle, 'Choose Your Controls'),
        controlButton1: parseLocalizableText(parameters.textControlButton1, 'Keyboard'),
        controlButton2: parseLocalizableText(parameters.textControlButton2, 'Mouse'),
        paused: parseLocalizableText(parameters.textPaused, 'PAUSED'),
        pausedSub: parseLocalizableText(parameters.textPausedSub, 'Ctrl: Exit | X/R-Click: Resume'),
        gameOver: parseLocalizableText(parameters.textGameOver, 'GAME OVER'),
        endContinue: parseLocalizableText(parameters.textEndContinue, 'Press any key to continue'),
        centerMessageOffsetX: Number(parameters.textCenterMessageOffsetX || 0),
        centerMessageOffsetY: Number(parameters.textCenterMessageOffsetY || 0),
    };

    const DefaultSettings = {
        width: Number(parameters['boardWidth'] || 25), height: Number(parameters['boardHeight'] || 20),
        speed: Number(parameters['startSpeed'] || 6), varId: Number(parameters['scoreVariableId'] || 1),
        snakeColor: parameters['defaultSnakeColor'] || '#00FFFF', wrapAround: false
    };
    const RandomSettings = {
        minW: Number(parameters['randomMinWidth'] || 15), maxW: Number(parameters['randomMaxWidth'] || 30),
        minH: Number(parameters['randomMinHeight'] || 15), maxH: Number(parameters['randomMaxHeight'] || 25),
        minS: Number(parameters['randomMinSpeed'] || 12), maxS: Number(parameters['randomMaxSpeed'] || 6)
    };
    const MOUSE_SUPPORT = parameters['enableMouseSupport'] === 'true';
    const backImage = parameters['backImage'] || '';

    const BoardOutline = {
        enabled: parameters['enableOutline'] === 'true',
        thickness: Number(parameters['outlineThickness'] || 3),
        color: PIXI.utils.string2hex(parameters['outlineColor'] || '#CCCCCC'),
        alpha: Number(parameters['outlineAlpha'] || 1.0)
    };

    const SPEED_INCREMENT = Number(parameters['speedIncrement'] || 0.2);
    const SPEED_EFFECT_DURATION = Number(parameters['speedEffectDuration'] || 300);
    const SPEED_UP_FACTOR = Number(parameters['speedUpFactor'] || 2);
    const SLOW_DOWN_FACTOR = Number(parameters['slowDownFactor'] || 0.5);
    const DIET_SHRINK_AMOUNT = Number(parameters['dietShrinkAmount'] || 2);

    const SUPER_FOOD_SCORE = Number(parameters['superFoodScore'] || 3);
    const SUPER_FOOD_GROWTH = Number(parameters['superFoodGrowth'] || 2);
    const SPEED_UP_FOOD_SCORE = Number(parameters['speedUpFoodScore'] || 1);
    const SLOW_DOWN_FOOD_SCORE = Number(parameters['slowDownFoodScore'] || 1);
    const DIET_FOOD_SCORE = Number(parameters['dietFoodScore'] || 2);
    const BARRIER_FOOD_SCORE = Number(parameters['barrierFoodScore'] || 1);
    
    const RED_CHANCE = Number(parameters['redFoodChance'] || 15);
    const GREEN_CHANCE = Number(parameters['greenFoodChance'] || 15);
    const PURPLE_CHANCE = Number(parameters['purpleFoodChance'] || 10);
    const BLACK_CHANCE = Number(parameters['blackFoodChance'] || 8);
    const WHITE_CHANCE = Number(parameters['whiteFoodChance'] || 7);
    const PINK_CHANCE = Number(parameters['pinkFoodChance'] || 5);

    const LIFESPAN_MAP = {
        SPEED_UP: Number(parameters['speedUpFoodLifespan'] || 0),
        SLOW_DOWN: Number(parameters['slowDownFoodLifespan'] || 0),
        SUPER: Number(parameters['superFoodLifespan'] || 3),
        POISON: Number(parameters['poisonFoodLifespan'] || 2),
        BARRIER: Number(parameters['barrierFoodLifespan'] || 4),
        DIET: Number(parameters['dietFoodLifespan'] || 5)
    };

    const SoundSettings = {
        confirm: parameters['seConfirm'] || '', cursor: parameters['seCursor'] || '',
        eat: parameters['seEat'] || '', gameOver: parameters['seGameOver'] || '',
        barrierBreak: parameters['seBarrierBreak'] || ''
    };
    
    const CELL_SIZE = 24;
    const BOARD_COLOR = 0x1D2B38, GRID_COLOR = 0x2C3E50, EYE_COLOR = 0xFFFFFF;
    const FOOD_TYPES = {
        NORMAL:    { color: '#D2691E', score: 1 }, SUPER:     { color: '#20B2AA', score: SUPER_FOOD_SCORE },
        SPEED_UP:  { color: '#FF4500', score: SPEED_UP_FOOD_SCORE }, SLOW_DOWN: { color: '#32CD32', score: SLOW_DOWN_FOOD_SCORE },
        POISON:    { color: '#000000', score: 0 }, BARRIER:   { color: '#FFFFFF', score: BARRIER_FOOD_SCORE },
        DIET:      { color: '#FFC0CB', score: DIET_FOOD_SCORE }
    };
    
    const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
    const getRandomFloat = (min, max) => Math.random() * (max - min) + min;

    const playCustomSound = (customSeName, fallbackSeFunction) => {
        if (customSeName) {
            AudioManager.playSe({ name: customSeName, volume: 90, pitch: 100, pan: 0 });
        } else { fallbackSeFunction.call(SoundManager); }
    };

	PluginManager.registerCommand(pluginName, "startSnake", args => {
    const settings = {
        width: Number(args.width) || DefaultSettings.width, 
        height: Number(args.height) || DefaultSettings.height,
        speed: Number(args.speed) || DefaultSettings.speed, 
        varId: Number(args.scoreVarId) || DefaultSettings.varId,
        snakeColor: args.snakeColor || DefaultSettings.snakeColor, 
        wrapAround: args.wrapAround === 'true',
        enableOutline: args.enableOutline !== undefined ? args.enableOutline === 'true' : BoardOutline.enabled,
        outlineThickness: Number(args.outlineThickness) || BoardOutline.thickness,
        outlineColor: args.outlineColor || parameters['outlineColor'] || '#CCCCCC',
        outlineAlpha: Number(args.outlineAlpha) !== undefined ? Number(args.outlineAlpha) : BoardOutline.alpha
    };
    SceneManager.push(Scene_Snake); 
    SceneManager.prepareNextScene(settings);
	});

PluginManager.registerCommand(pluginName, "startCustomRandomSnake", args => {
    const minWidth = (args.minWidth && Number(args.minWidth) > 0) ? Number(args.minWidth) : RandomSettings.minW;
    const maxWidth = (args.maxWidth && Number(args.maxWidth) > 0) ? Number(args.maxWidth) : RandomSettings.maxW;
    const minHeight = (args.minHeight && Number(args.minHeight) > 0) ? Number(args.minHeight) : RandomSettings.minH;
    const maxHeight = (args.maxHeight && Number(args.maxHeight) > 0) ? Number(args.maxHeight) : RandomSettings.maxH;
    const minSpeed = (args.minSpeed && Number(args.minSpeed) > 0) ? Number(args.minSpeed) : RandomSettings.minS;
    const maxSpeed = (args.maxSpeed && Number(args.maxSpeed) > 0) ? Number(args.maxSpeed) : RandomSettings.maxS;
    
    const finalMinWidth = Math.min(minWidth, maxWidth);
    const finalMaxWidth = Math.max(minWidth, maxWidth);
    const finalMinHeight = Math.min(minHeight, maxHeight);
    const finalMaxHeight = Math.max(minHeight, maxHeight);
    const finalMinSpeed = Math.max(minSpeed, maxSpeed);
    const finalMaxSpeed = Math.min(minSpeed, maxSpeed);
    
    const settings = {
        width: getRandomInt(finalMinWidth, finalMaxWidth), 
        height: getRandomInt(finalMinHeight, finalMaxHeight),
        speed: getRandomFloat(finalMaxSpeed, finalMinSpeed), 
        varId: Number(args.scoreVarId) || DefaultSettings.varId,
        snakeColor: (args.snakeColor && args.snakeColor.trim() !== '') ? args.snakeColor : DefaultSettings.snakeColor, 
        wrapAround: args.wrapAround === 'true',
        enableOutline: args.enableOutline !== undefined ? args.enableOutline === 'true' : BoardOutline.enabled,
        outlineThickness: Number(args.outlineThickness) || BoardOutline.thickness,
        outlineColor: (args.outlineColor && args.outlineColor.trim() !== '') ? args.outlineColor : (parameters['outlineColor'] || '#CCCCCC'),
        outlineAlpha: Number(args.outlineAlpha) !== undefined ? Number(args.outlineAlpha) : BoardOutline.alpha
    };
    
    SceneManager.push(Scene_Snake); 
    SceneManager.prepareNextScene(settings);
});

    class Scene_Snake extends Scene_Base {
        prepare(settings) { this._settings = settings; }
        create() { super.create(); this.createBackground(); this.createGameWindow(); this.initGame(); }

        initGame() {
            this._score = 0; this._gameOver = false; this._isPaused = false;
            this._controlChoiceMade = false; this._useMouseControls = false; this._choiceIndex = 0;
            this._barrierActive = false; this._isDying = false; this._gameOverDelay = 0;
            const startX = Math.floor(this._settings.width / 2); const startY = Math.floor(this._settings.height / 2);
            this._snake = [{ x: startX, y: startY }, { x: startX - 1, y: startY }, { x: startX - 2, y: startY }];
            this._direction = { x: 1, y: 0 }; this._nextDirection = { x: 1, y: 0 };
            this._baseSpeed = this._settings.speed; this._currentSpeed = this._settings.speed;
            this._speedEffectTimer = 0; this._moveCounter = 0; this._glowCounter = 0;
            this._mainFoodsEaten = 0; this._foods = []; this.spawnBrownFood();
            this._effects = [];
            this._gameOverText.visible = false; this._pauseText.visible = false; this._pauseExitText.visible = false;
            this.createControlChoiceScreen(); this.drawAll();
        }

        createBackground() {
            this._backgroundSprite = new Sprite();
            if (backImage && backImage.trim() !== '') this._backgroundSprite.bitmap = ImageManager.loadPicture(backImage);
            else this._backgroundSprite.bitmap = SceneManager.backgroundBitmap();
            this.addChild(this._backgroundSprite);
        }

        createGameWindow() {
            this._gameWidth = this._settings.width * CELL_SIZE; this._gameHeight = this._settings.height * CELL_SIZE;
            const x = (Graphics.boxWidth - this._gameWidth) / 2, y = (Graphics.boxHeight - this._gameHeight) / 2;
            this._gameContainer = new Sprite(); this._gameContainer.x = x; this._gameContainer.y = y; this.addChild(this._gameContainer);
            this._graphics = new PIXI.Graphics(); this._gameContainer.addChild(this._graphics);
            this._effectsGraphics = new PIXI.Graphics(); this._gameContainer.addChild(this._effectsGraphics);
            
            this.createInfoText();
            this.createCenterText();
        }
        
        createInfoText() {
            const infoStyle = { fontFamily: 'GameFont', fontSize: 28, fill: '#FFFFFF', stroke: '#000000', strokeThickness: 4 };
            this._scoreText = new PIXI.Text('', infoStyle);
            this._scoreText.x = 20 + uiTexts.scoreOffsetX;
            this._scoreText.y = 20 + uiTexts.scoreOffsetY;
            this.addChild(this._scoreText);
            
            this._effectText = new PIXI.Text('', { ...infoStyle, align: 'center' });
            this._effectText.anchor.set(0.5, 0);
            this._effectText.x = Graphics.width / 2 + uiTexts.effectOffsetX;
            this._effectText.y = 20 + uiTexts.effectOffsetY;
            this.addChild(this._effectText);
        }

        createCenterText() {
            const gameOverStyle = { fontFamily: 'GameFont', fontSize: 48, fill: 'white', stroke: 'red', strokeThickness: 4, align: 'center', wordWrap: true, wordWrapWidth: this._gameWidth - 20 };
            this._gameOverText = new PIXI.Text('', gameOverStyle);
            this._gameOverText.anchor.set(0.5);
            this._gameOverText.x = this._gameWidth / 2 + uiTexts.centerMessageOffsetX;
            this._gameOverText.y = this._gameHeight / 2 + uiTexts.centerMessageOffsetY;
            this._gameContainer.addChild(this._gameOverText);
            
            const pauseStyle = { fontFamily: 'GameFont', fontSize: 40, fill: 'white', align: 'center' };
            this._pauseText = new PIXI.Text(uiTexts.paused, pauseStyle);
            this._pauseText.anchor.set(0.5);
            this._pauseText.x = this._gameWidth / 2 + uiTexts.centerMessageOffsetX;
            this._pauseText.y = this._gameHeight / 2 - 20 + uiTexts.centerMessageOffsetY;
            this._gameContainer.addChild(this._pauseText);
            
            const pauseSubStyle = { fontFamily: 'GameFont', fontSize: 22, fill: '#cccccc' };
            this._pauseExitText = new PIXI.Text(uiTexts.pausedSub, pauseSubStyle);
            this._pauseExitText.anchor.set(0.5);
            this._pauseExitText.x = this._gameWidth / 2 + uiTexts.centerMessageOffsetX;
            this._pauseExitText.y = this._gameHeight / 2 + 30 + uiTexts.centerMessageOffsetY;
            this._gameContainer.addChild(this._pauseExitText);
        }

        createControlChoiceScreen() {
            this._choiceButtons = []; this._choiceOverlay = new PIXI.Container(); this.addChild(this._choiceOverlay);
            const overlayBg = new PIXI.Graphics();
            overlayBg.beginFill(0x000000, 0.7).drawRect(0, 0, Graphics.width, Graphics.height).endFill(); this._choiceOverlay.addChild(overlayBg);
            const title = new PIXI.Text(uiTexts.controlTitle, { fontFamily: 'GameFont', fontSize: 48, fill: 'white', align: 'center' });
            title.anchor.set(0.5); title.x = Graphics.width / 2; title.y = Graphics.height / 2 - 100; this._choiceOverlay.addChild(title);
            const buttonWidth = 200, spacing = 20;
            const keyboardButton = this.createButton(uiTexts.controlButton1, -buttonWidth/2 - spacing, 0);
            this._choiceButtons.push(keyboardButton); this._choiceOverlay.addChild(keyboardButton);
            if (MOUSE_SUPPORT) {
                const mouseButton = this.createButton(uiTexts.controlButton2, buttonWidth/2 + spacing, 0);
                this._choiceButtons.push(mouseButton); this._choiceOverlay.addChild(mouseButton);
            } else { keyboardButton.x = 0; }
            this._choiceButtons.forEach(btn => { btn.x += Graphics.width / 2; btn.y += Graphics.height / 2; });
            this.updateButtonHighlights();
        }

        createButton(text, x, y) {
            const container = new PIXI.Container(); container.x = x; container.y = y;
            container.interactive = true; container.buttonMode = true; container.bg = new PIXI.Graphics();
            container.addChild(container.bg);
            const label = new PIXI.Text(text, { fontFamily: 'GameFont', fontSize: 32, fill: 'white' });
            label.anchor.set(0.5); container.addChild(label);
            return container;
        }

        updateButtonHighlights() {
            const width = 200, height = 60;
            this._choiceButtons.forEach((button, index) => {
                button.bg.clear();
                if (index === this._choiceIndex) button.bg.lineStyle(4, 0xFFFFFF, 1).beginFill(0x333333, 0.7);
                else button.bg.lineStyle(2, 0xCCCCCC, 1).beginFill(0x000000, 0.5);
                button.bg.drawRoundedRect(-width/2, -height/2, width, height, 15).endFill();
            });
        }

        update() {
            super.update(); this._glowCounter++;
            if (!this._controlChoiceMade) { this.handleControlChoiceInput(); this.drawAll(); return; }
            if (this._isPaused) {
                if (Input.isTriggered('control')) {
                    $gameVariables.setValue(this._settings.varId, this._score);
                    this.popScene();
                } else if (Input.isTriggered('cancel') || TouchInput.isCancelled()) {
                    this._isPaused = false;
                    this._pauseText.visible = false;
                    this._pauseExitText.visible = false;
                    playCustomSound(SoundSettings.cursor, SoundManager.playCursor);
                }
                return;
            }
            if (Input.isTriggered('control')) {
                if (!this._gameOver && !this._isDying) {
                    this._isPaused = true;
                    this._pauseText.visible = true;
                    this._pauseExitText.visible = true;
                    playCustomSound(SoundSettings.confirm, SoundManager.playOk);
                }
                return;
            }
            if (this._gameOver) {
                if (Input.isTriggered('ok') || Input.isTriggered('cancel') || TouchInput.isTriggered()) {
                    $gameVariables.setValue(this._settings.varId, this._score); this.popScene();
                }
            } else if (this._isDying) {
                this._gameOverDelay--;
                if (this._gameOverDelay <= 0) {
                    this._isDying = false; this._gameOver = true;
                    this._gameOverText.text = `${uiTexts.gameOver}\n\n${uiTexts.endContinue}`;
                    this._gameOverText.visible = true;
                }
            } else {
                this.updateEffects();
                this.handleInput();
                if (this._speedEffectTimer > 0) {
                    this._speedEffectTimer--;
                    if (this._speedEffectTimer === 0) this._currentSpeed = this._baseSpeed;
                }
                this._moveCounter++;
                if (this._moveCounter >= this._currentSpeed) {
                    this._moveCounter = 0; this.updateSnake();
                }
            }
            this.drawAll();
        }

        handleControlChoiceInput() {
            if (Input.isTriggered('right')) {
                this._choiceIndex = (this._choiceIndex + 1) % this._choiceButtons.length;
                playCustomSound(SoundSettings.cursor, SoundManager.playCursor); this.updateButtonHighlights();
            } else if (Input.isTriggered('left')) {
                this._choiceIndex = (this._choiceIndex - 1 + this._choiceButtons.length) % this._choiceButtons.length;
                playCustomSound(SoundSettings.cursor, SoundManager.playCursor); this.updateButtonHighlights();
            } else if (Input.isTriggered('ok')) { this.selectControls(this._choiceIndex === 1);
            } else if (TouchInput.isTriggered()) {
                this._choiceButtons.forEach((button, index) => {
                    if (this.isPointInContainer(TouchInput, button)) this.selectControls(index === 1);
                });
            }
        }

        selectControls(useMouse) {
            this._useMouseControls = useMouse; this._controlChoiceMade = true;
            this._choiceOverlay.visible = false; this._moveCounter = 0; this._glowCounter = 0;
            playCustomSound(SoundSettings.confirm, SoundManager.playOk);
        }

        updateSnake() {
            this._direction = this._nextDirection;
            const head = this._snake[0];
            let newHead = { x: head.x + this._direction.x, y: head.y + this._direction.y };
            const wallCollision = !this._settings.wrapAround && (newHead.x < 0 || newHead.x >= this._settings.width || newHead.y < 0 || newHead.y >= this._settings.height);
            const selfCollision = this._snake.some(segment => segment.x === newHead.x && segment.y === newHead.y);

            if (wallCollision || selfCollision) {
                if (this._barrierActive) {
                    this._barrierActive = false; playCustomSound(SoundSettings.barrierBreak, SoundManager.playRecovery);
                    if (wallCollision) {
                        if (newHead.x < 0) newHead.x = this._settings.width - 1; if (newHead.x >= this._settings.width) newHead.x = 0;
                        if (newHead.y < 0) newHead.y = this._settings.height - 1; if (newHead.y >= this._settings.height) newHead.y = 0;
                    }
                } else { this.triggerGameOver(); return; }
            } else if (this._settings.wrapAround) {
                if (newHead.x < 0) newHead.x = this._settings.width - 1; if (newHead.x >= this._settings.width) newHead.x = 0;
                if (newHead.y < 0) newHead.y = this._settings.height - 1; if (newHead.y >= this._settings.height) newHead.y = 0;
            }

            this._snake.unshift(newHead);
            const eatenFoodIndex = this._foods.findIndex(f => f.x === newHead.x && f.y === newHead.y);
            if (eatenFoodIndex > -1) { this.onEatFood(eatenFoodIndex); } else { this._snake.pop(); }
        }

        onEatFood(foodIndex) {
            const eatenFood = this._foods[foodIndex];
            this._foods.splice(foodIndex, 1);
            if (eatenFood.type === 'POISON') { this.triggerGameOver(); return; }
            this._score += FOOD_TYPES[eatenFood.type].score; playCustomSound(SoundSettings.eat, SoundManager.playEquip);
            
            if (eatenFood.type === 'DIET') {
                for(let i = 0; i < DIET_SHRINK_AMOUNT; i++) if (this._snake.length > 3) this._snake.pop();
            } else if (eatenFood.type === 'SUPER') {
                this.createSplashEffect(eatenFood.x, eatenFood.y);
                const tail = this._snake[this._snake.length - 1];
                const extraGrowth = Math.max(0, SUPER_FOOD_GROWTH - 1);
                for (let i = 0; i < extraGrowth; i++) this._snake.push({ ...tail });
            }
            
            if (eatenFood.type === 'NORMAL') {
                this._mainFoodsEaten++; this._baseSpeed = Math.max(1, this._baseSpeed - SPEED_INCREMENT);
                if (this._speedEffectTimer === 0) this._currentSpeed = this._baseSpeed;
                this._foods = this._foods.filter(food => {
                    if (food.type === 'NORMAL' || !food.hasOwnProperty('spawnedAt')) return true;
                    const lifespan = LIFESPAN_MAP[food.type]; if (lifespan <= 0) return true;
                    return (this._mainFoodsEaten - food.spawnedAt) < lifespan;
                });
                this.spawnBrownFood(); this.trySpawnSpecialFoods();
            } else if (eatenFood.type === 'SPEED_UP') {
                this._currentSpeed = Math.max(1, this._baseSpeed / SPEED_UP_FACTOR);
                this._speedEffectTimer = SPEED_EFFECT_DURATION;
            } else if (eatenFood.type === 'SLOW_DOWN') {
                this._currentSpeed = this._baseSpeed / SLOW_DOWN_FACTOR;
                this._speedEffectTimer = SPEED_EFFECT_DURATION;
            } else if (eatenFood.type === 'BARRIER') { this._barrierActive = true; }
        }

        drawAll() {
            this._graphics.clear();
            this._effectsGraphics.clear();
            this.drawBoard();
            this.drawFood();
            this.drawSnake();
            this.drawEffects();
            this.drawInfo();
            if (this._gameOver) this.drawGameOver(); else if (this._isPaused) this.drawPaused();
        }

		drawBoard() {
			this._graphics.beginFill(BOARD_COLOR, 1).drawRect(0, 0, this._gameWidth, this._gameHeight).endFill();
			if (this._settings.enableOutline) {
				const outlineColor = PIXI.utils.string2hex(this._settings.outlineColor);
				this._graphics.lineStyle(this._settings.outlineThickness, outlineColor, this._settings.outlineAlpha)
					.drawRect(0, 0, this._gameWidth, this._gameHeight);
			}
			this._graphics.lineStyle(1, GRID_COLOR, 1);
			for (let x=1; x < this._settings.width; x++) 
				this._graphics.moveTo(x * CELL_SIZE, 0).lineTo(x * CELL_SIZE, this._gameHeight);
			for (let y=1; y < this._settings.height; y++) 
				this._graphics.moveTo(0, y * CELL_SIZE).lineTo(this._gameWidth, y * CELL_SIZE);
		}

        drawSnake() {
            const headColor = PIXI.utils.string2hex(this._settings.snakeColor);
            const bodyStartColor = this._darkenColor(headColor, 0.1); const bodyEndColor = this._darkenColor(headColor, 0.4);
            const snakeLen = this._snake.length;
            for (let i = snakeLen - 1; i > 0; i--) {
                const seg = this._snake[i]; const cx = seg.x * CELL_SIZE + CELL_SIZE / 2; const cy = seg.y * CELL_SIZE + CELL_SIZE / 2;
                const factor = (i - 1) / Math.max(1, snakeLen - 2);
                const color = this._interpolateColor(bodyStartColor, bodyEndColor, factor);
                this._graphics.beginFill(color).drawCircle(cx, cy, CELL_SIZE * 0.45).endFill();
            }
            const head = this._snake[0]; const hx = head.x * CELL_SIZE + CELL_SIZE / 2; const hy = head.y * CELL_SIZE + CELL_SIZE / 2;
            if (this._barrierActive) {
                const waveFactor = (Math.sin(this._glowCounter * 0.15) + 1) / 2; const auraColor = 0xFFFFFF;
                const minRadius = CELL_SIZE * 0.6; const maxRadius = CELL_SIZE * 0.8;
                const currentRadius = minRadius + (maxRadius - minRadius) * waveFactor;
                this._graphics.beginFill(auraColor, 0.4).drawCircle(hx, hy, currentRadius).endFill();
            }
            this._graphics.beginFill(headColor).drawCircle(hx, hy, CELL_SIZE * 0.5).endFill();
            const eyeRadius = CELL_SIZE * 0.1, eyeOffset = CELL_SIZE * 0.2; let eye1X, eye1Y, eye2X, eye2Y;
            if (this._direction.x !== 0) {
                eye1X = hx + eyeOffset * -this._direction.x; eye1Y = hy - eyeOffset;
                eye2X = hx + eyeOffset * -this._direction.x; eye2Y = hy + eyeOffset;
            } else {
                eye1X = hx - eyeOffset; eye1Y = hy + eyeOffset * -this._direction.y;
                eye2X = hx + eyeOffset; eye2Y = hy + eyeOffset * -this._direction.y;
            }
            this._graphics.beginFill(EYE_COLOR).drawCircle(eye1X, eye1Y, eyeRadius).drawCircle(eye2X, eye2Y, eyeRadius).endFill();
        }

        drawFood() {
            const waveFactor = (Math.sin(this._glowCounter * 0.1) + 1) / 2;
            for (const food of this._foods) {
                const cx = food.x * CELL_SIZE + CELL_SIZE / 2, cy = food.y * CELL_SIZE + CELL_SIZE / 2;
                const color = PIXI.utils.string2hex(FOOD_TYPES[food.type].color);
                const minGlowRadius = CELL_SIZE * 0.5, maxGlowRadius = CELL_SIZE * 0.8;
                const currentGlowRadius = minGlowRadius + (maxGlowRadius - minGlowRadius) * waveFactor;
                this._graphics.beginFill(color, 0.3).drawCircle(cx, cy, currentGlowRadius).endFill();
                this._graphics.beginFill(color, 1).drawCircle(cx, cy, CELL_SIZE * 0.35).endFill();
            }
        }
        
        drawInfo() {
            this._scoreText.text = `${uiTexts.scoreLabel}${this._score}`;
            if (this._speedEffectTimer > 0) {
                const isFast = this._currentSpeed < this._baseSpeed;
                this._effectText.style.fill = isFast ? '#FF4444' : '#44FF44';
                this._effectText.text = isFast ? uiTexts.speedUp : uiTexts.slowDown;
            } else {
                this._effectText.text = '';
            }
        }

        isPointInContainer(point, container) {
            const localPoint = container.toLocal(new PIXI.Point(point.x, point.y));
            return container.getLocalBounds().contains(localPoint.x, localPoint.y);
        }

        handleInput() { if (this._useMouseControls) this.handleMouseInput(); else this.handleKeyboardInput(); }

        handleKeyboardInput() {
            if (Input.isPressed('up') && this._direction.y === 0) this._nextDirection = { x: 0, y: -1 };
            else if (Input.isPressed('down') && this._direction.y === 0) this._nextDirection = { x: 0, y: 1 };
            else if (Input.isPressed('left') && this._direction.x === 0) this._nextDirection = { x: -1, y: 0 };
            else if (Input.isPressed('right') && this._direction.x === 0) this._nextDirection = { x: 1, y: 0 };
        }

        handleMouseInput() {
            const head = this._snake[0];
            const headScreenX = this._gameContainer.x + (head.x * CELL_SIZE + CELL_SIZE / 2);
            const headScreenY = this._gameContainer.y + (head.y * CELL_SIZE + CELL_SIZE / 2);
            const mouseX = TouchInput.x; const mouseY = TouchInput.y;
            const deltaX = mouseX - headScreenX; const deltaY = mouseY - headScreenY;
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                if (deltaX > 0 && this._direction.x === 0) this._nextDirection = { x: 1, y: 0 };
                else if (deltaX < 0 && this._direction.x === 0) this._nextDirection = { x: -1, y: 0 };
            } else {
                if (deltaY > 0 && this._direction.y === 0) this._nextDirection = { x: 0, y: 1 };
                else if (deltaY < 0 && this._direction.y === 0) this._nextDirection = { x: 0, y: -1 };
            }
        }

        findEmptySpot() {
            let x, y;
            do {
                x = Math.floor(Math.random() * this._settings.width); y = Math.floor(Math.random() * this._settings.height);
            } while (this._snake.some(s => s.x === x && s.y === y) || this._foods.some(f => f.x === x && f.y === y));
            return { x, y };
        }

        spawnBrownFood() { this._foods.push({ ...this.findEmptySpot(), type: 'NORMAL' }); }
        
        trySpawnSpecialFoods() {
            const spawnedAt = this._mainFoodsEaten;
            if (Math.random() * 100 < RED_CHANCE) this._foods.push({ ...this.findEmptySpot(), type: 'SPEED_UP', spawnedAt });
            if (Math.random() * 100 < GREEN_CHANCE) this._foods.push({ ...this.findEmptySpot(), type: 'SLOW_DOWN', spawnedAt });
            if (Math.random() * 100 < PURPLE_CHANCE) this._foods.push({ ...this.findEmptySpot(), type: 'SUPER', spawnedAt });
            if (Math.random() * 100 < BLACK_CHANCE) this._foods.push({ ...this.findEmptySpot(), type: 'POISON', spawnedAt });
            if (Math.random() * 100 < WHITE_CHANCE) this._foods.push({ ...this.findEmptySpot(), type: 'BARRIER', spawnedAt });
            if (Math.random() * 100 < PINK_CHANCE) this._foods.push({ ...this.findEmptySpot(), type: 'DIET', spawnedAt });
        }
        
        triggerGameOver() {
            if (this._gameOver || this._isDying) return;
            this._isDying = true; this._gameOverDelay = 30;
            playCustomSound(SoundSettings.gameOver, SoundManager.playBuzzer);
        }

        drawGameOver() { this._graphics.beginFill(0x000000, 0.5).drawRect(0, this._gameHeight/2 - 60, this._gameWidth, 120).endFill(); }
        drawPaused() { this._graphics.beginFill(0x000000, 0.5).drawRect(0, this._gameHeight/2 - 50, this._gameWidth, 100).endFill(); }

        _darkenColor(color, amount) {
            let r = (color >> 16) & 0xff, g = (color >> 8) & 0xff, b = color & 0xff;
            r = Math.max(0, r * (1 - amount)); g = Math.max(0, g * (1 - amount)); b = Math.max(0, b * (1 - amount));
            return (r << 16) | (g << 8) | b;
        }

        _interpolateColor(color1, color2, factor) {
            const r1 = (color1 >> 16) & 0xff, g1 = (color1 >> 8) & 0xff, b1 = color1 & 0xff;
            const r2 = (color2 >> 16) & 0xff, g2 = (color2 >> 8) & 0xff, b2 = color2 & 0xff;
            const r = Math.round(r1 + (r2 - r1) * factor), g = Math.round(g1 + (g2 - g1) * factor), b = Math.round(b1 + (b2 - b1) * factor);
            return (r << 16) | (g << 8) | b;
        }

        createSplashEffect(gridX, gridY) {
            const screenX = gridX * CELL_SIZE + CELL_SIZE / 2;
            const screenY = gridY * CELL_SIZE + CELL_SIZE / 2;
            const particleCount = 20;
            const color = PIXI.utils.string2hex(FOOD_TYPES.SUPER.color);

            for (let i = 0; i < particleCount; i++) {
                const angle = Math.random() * Math.PI * 2;
                const speed = 2 + Math.random() * 3;
                const life = 30 + Math.random() * 20;
                this._effects.push({
                    x: screenX, y: screenY,
                    vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed,
                    life: life, maxLife: life,
                    color: color,
                    size: 3 + Math.random() * 4
                });
            }
        }

        updateEffects() {
            for (let i = this._effects.length - 1; i >= 0; i--) {
                const p = this._effects[i];
                p.x += p.vx;
                p.y += p.vy;
                p.life--;
                if (p.life <= 0) {
                    this._effects.splice(i, 1);
                }
            }
        }

        drawEffects() {
            for (const p of this._effects) {
                const alpha = Math.max(0, p.life / p.maxLife);
                this._effectsGraphics.beginFill(p.color, alpha);
                this._effectsGraphics.drawCircle(p.x, p.y, p.size);
                this._effectsGraphics.endFill();
            }
        }
    }
})();