712 lines
39 KiB
YAML
712 lines
39 KiB
YAML
# Knobuss - optional carousel screen (base id 10). A Gyruss-style tube shooter: your ship orbits the rim, you
|
|
# auto-fire inward, foes spiral out from the centre. Turn the knob to aim, blast them before they
|
|
# reach you. Remove it: drop this file from your config's package "files:" list. Nothing else to edit.
|
|
# Scores (g_gy_top / g_gy_best) + the "Reset scores" settings action live here - fully self-contained.
|
|
#
|
|
# Perf: ship + bullet pool (6) + enemy pool (10) are all positioned each tick from float arrays in
|
|
# ONE lambda (lv_obj_set_pos over a pooled lv_obj_t* array - LVGL can't index a widget by a runtime
|
|
# var), so per-tick cost is constant and the action chain stays shallow. Sprites have a fixed src
|
|
# (enemies alternate enemy/enemy2 by slot), so there are no per-tick image swaps.
|
|
|
|
esphome:
|
|
on_boot:
|
|
- priority: 655
|
|
then:
|
|
- lambda: 'id(g_present)[10] = true;'
|
|
- priority: 650
|
|
then:
|
|
- lambda: |-
|
|
int g = id(g_wgrp_n)++;
|
|
id(g_wgrp_labels) += (g ? "\n" : ""); id(g_wgrp_labels) += "Knobuss";
|
|
int o = id(g_wopt_n)++;
|
|
id(g_wopt_group)[o] = g; id(g_wopt_label)[o] = "Reset scores"; id(g_wopt_kind)[o] = 1; id(g_wopt_ptr)[o] = &id(g_gy_reset);
|
|
|
|
globals:
|
|
- id: g_gy_best
|
|
type: int
|
|
restore_value: yes
|
|
initial_value: '0'
|
|
- id: g_gy_top
|
|
type: int[10]
|
|
restore_value: yes
|
|
- id: g_gy_reset
|
|
type: bool
|
|
restore_value: no
|
|
initial_value: 'false'
|
|
- id: g_gy_state # 0=menu 1=playing 2=game over 3=how-to 4=scores
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '0'
|
|
- id: g_gy_score
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '0'
|
|
- id: g_gy_lives
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '3'
|
|
- id: g_gy_ship_ang # ship orbit angle (radians)
|
|
type: float
|
|
restore_value: no
|
|
initial_value: '0.0'
|
|
- id: g_gy_dead
|
|
type: bool
|
|
restore_value: no
|
|
initial_value: 'false'
|
|
- id: g_gy_fire_clk
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '0'
|
|
- id: g_gy_spawn_clk
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '0'
|
|
- id: g_gy_hud_dirty # score/lives changed this tick -> refresh the HUD labels
|
|
type: bool
|
|
restore_value: no
|
|
initial_value: 'false'
|
|
# bullet pool (fly inward along a fixed radial)
|
|
- id: g_gy_br # radius (px from centre)
|
|
type: float[6]
|
|
restore_value: no
|
|
- id: g_gy_ba # angle (radians)
|
|
type: float[6]
|
|
restore_value: no
|
|
- id: g_gy_bon
|
|
type: bool[6]
|
|
restore_value: no
|
|
# enemy pool (spiral outward from the centre)
|
|
- id: g_gy_er
|
|
type: float[10]
|
|
restore_value: no
|
|
- id: g_gy_ea
|
|
type: float[10]
|
|
restore_value: no
|
|
- id: g_gy_eon
|
|
type: bool[10]
|
|
restore_value: no
|
|
# starfield (depth illusion): dots rush outward from the centre, faster as they near the rim
|
|
- id: g_gy_sr # radius (px from centre)
|
|
type: float[10]
|
|
restore_value: no
|
|
- id: g_gy_sa # angle (radians)
|
|
type: float[10]
|
|
restore_value: no
|
|
# explosion pool (expanding shockwave ring of dots) - fired when a foe reaches the rim
|
|
- id: g_gy_exx # centre X (relative to screen centre, px)
|
|
type: float[2]
|
|
restore_value: no
|
|
- id: g_gy_exy # centre Y
|
|
type: float[2]
|
|
restore_value: no
|
|
- id: g_gy_exage # -1 = inactive, else explosion frame 0..7
|
|
type: int[2]
|
|
restore_value: no
|
|
- id: g_gy_shipframe # current ship rotation frame (0..15), -1 forces a refresh
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '-1'
|
|
- id: g_gy_shipdirty # ship rotation bucket changed this tick
|
|
type: bool
|
|
restore_value: no
|
|
initial_value: 'false'
|
|
- id: g_gy_exact # an explosion is active or expired this tick (refresh its sprite)
|
|
type: bool
|
|
restore_value: no
|
|
initial_value: 'false'
|
|
- id: g_gy_tick # frame counter (throttles the starfield + ship-sprite refresh)
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '0'
|
|
|
|
script:
|
|
# position every active widget from the float arrays (constant cost, one shallow lambda)
|
|
- id: gy_render
|
|
then:
|
|
- lambda: |-
|
|
const float R = 148.0f;
|
|
// starfield (background depth) - small dots rushing outward
|
|
lv_obj_t *SF[6] = { id(gy_s0), id(gy_s1), id(gy_s2), id(gy_s3), id(gy_s4), id(gy_s5) };
|
|
for (int k = 0; k < 6; k++) {
|
|
float r = id(g_gy_sr)[k], a = id(g_gy_sa)[k];
|
|
lv_obj_set_pos(SF[k], 180 + (int) lroundf(cosf(a) * r) - 1, 180 + (int) lroundf(sinf(a) * r) - 1);
|
|
}
|
|
lv_obj_t *BB[6] = { id(gy_b0), id(gy_b1), id(gy_b2), id(gy_b3), id(gy_b4), id(gy_b5) };
|
|
for (int k = 0; k < 6; k++) {
|
|
if (id(g_gy_bon)[k]) {
|
|
float r = id(g_gy_br)[k], a = id(g_gy_ba)[k];
|
|
lv_obj_set_pos(BB[k], 180 + (int) lroundf(cosf(a) * r) - 6, 180 + (int) lroundf(sinf(a) * r) - 6);
|
|
} else lv_obj_set_pos(BB[k], 400, 400);
|
|
}
|
|
lv_obj_t *EE[10] = { id(gy_e0), id(gy_e1), id(gy_e2), id(gy_e3), id(gy_e4), id(gy_e5), id(gy_e6), id(gy_e7), id(gy_e8), id(gy_e9) };
|
|
for (int k = 0; k < 10; k++) {
|
|
if (id(g_gy_eon)[k]) {
|
|
float r = id(g_gy_er)[k], a = id(g_gy_ea)[k];
|
|
lv_obj_set_pos(EE[k], 180 + (int) lroundf(cosf(a) * r) - 13, 180 + (int) lroundf(sinf(a) * r) - 13);
|
|
} else lv_obj_set_pos(EE[k], 400, 400);
|
|
}
|
|
float sa = id(g_gy_ship_ang);
|
|
lv_obj_set_pos(id(gy_ship), 180 + (int) lroundf(cosf(sa) * R) - 18, 180 + (int) lroundf(sinf(sa) * R) - 18);
|
|
// explosions (32px sprites) - positioned while alive, parked otherwise
|
|
lv_obj_t *XX[2] = { id(gy_x0), id(gy_x1) };
|
|
for (int s = 0; s < 2; s++) {
|
|
if (id(g_gy_exage)[s] >= 0) lv_obj_set_pos(XX[s], 180 + (int) lroundf(id(g_gy_exx)[s]) - 16, 180 + (int) lroundf(id(g_gy_exy)[s]) - 16);
|
|
else lv_obj_set_pos(XX[s], 400, 400);
|
|
}
|
|
# set the ship sprite to its current rotation frame (called only when the bucket changes)
|
|
- id: gy_shipspr
|
|
then:
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==0;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship0 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==1;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship1 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==2;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship2 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==3;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship3 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==4;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship4 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==5;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship5 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==6;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship6 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==7;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship7 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==8;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship8 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==9;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship9 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==10;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship10 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==11;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship11 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==12;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship12 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==13;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship13 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==14;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship14 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_shipframe)==15;' }, then: [ lvgl.image.update: { id: gy_ship, src: img_gy_ship15 } ] }
|
|
# advance/hide the explosion sprites by their frame (called only while a burst is live)
|
|
- id: gy_explspr
|
|
then:
|
|
- if:
|
|
condition: { lambda: 'return id(g_gy_exage)[0] < 0;' }
|
|
then: [ lvgl.widget.hide: gy_x0 ]
|
|
else:
|
|
- lvgl.widget.show: gy_x0
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[0]==0;' }, then: [ lvgl.image.update: { id: gy_x0, src: img_gy_expl0 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[0]==1;' }, then: [ lvgl.image.update: { id: gy_x0, src: img_gy_expl1 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[0]==2;' }, then: [ lvgl.image.update: { id: gy_x0, src: img_gy_expl2 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[0]==3;' }, then: [ lvgl.image.update: { id: gy_x0, src: img_gy_expl3 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[0]==4;' }, then: [ lvgl.image.update: { id: gy_x0, src: img_gy_expl4 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[0]==5;' }, then: [ lvgl.image.update: { id: gy_x0, src: img_gy_expl5 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[0]==6;' }, then: [ lvgl.image.update: { id: gy_x0, src: img_gy_expl6 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[0]>=7;' }, then: [ lvgl.image.update: { id: gy_x0, src: img_gy_expl7 } ] }
|
|
- if:
|
|
condition: { lambda: 'return id(g_gy_exage)[1] < 0;' }
|
|
then: [ lvgl.widget.hide: gy_x1 ]
|
|
else:
|
|
- lvgl.widget.show: gy_x1
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[1]==0;' }, then: [ lvgl.image.update: { id: gy_x1, src: img_gy_expl0 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[1]==1;' }, then: [ lvgl.image.update: { id: gy_x1, src: img_gy_expl1 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[1]==2;' }, then: [ lvgl.image.update: { id: gy_x1, src: img_gy_expl2 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[1]==3;' }, then: [ lvgl.image.update: { id: gy_x1, src: img_gy_expl3 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[1]==4;' }, then: [ lvgl.image.update: { id: gy_x1, src: img_gy_expl4 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[1]==5;' }, then: [ lvgl.image.update: { id: gy_x1, src: img_gy_expl5 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[1]==6;' }, then: [ lvgl.image.update: { id: gy_x1, src: img_gy_expl6 } ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exage)[1]>=7;' }, then: [ lvgl.image.update: { id: gy_x1, src: img_gy_expl7 } ] }
|
|
- id: gy_hud
|
|
then:
|
|
- lvgl.label.update: { id: lbl_gy_score, text: !lambda 'char b[12]; snprintf(b,sizeof(b),"%d",id(g_gy_score)); return std::string(b);' }
|
|
- lvgl.label.update: { id: lbl_gy_lives, text: !lambda 'char b[16]; snprintf(b,sizeof(b),"LIVES %d",id(g_gy_lives)<0?0:id(g_gy_lives)); return std::string(b);' }
|
|
# park every gameplay widget off-screen
|
|
- id: gy_clear
|
|
then:
|
|
- lambda: |-
|
|
lv_obj_t *BB[6] = { id(gy_b0), id(gy_b1), id(gy_b2), id(gy_b3), id(gy_b4), id(gy_b5) };
|
|
for (int k = 0; k < 6; k++) lv_obj_set_pos(BB[k], 400, 400);
|
|
lv_obj_t *EE[10] = { id(gy_e0), id(gy_e1), id(gy_e2), id(gy_e3), id(gy_e4), id(gy_e5), id(gy_e6), id(gy_e7), id(gy_e8), id(gy_e9) };
|
|
for (int k = 0; k < 10; k++) lv_obj_set_pos(EE[k], 400, 400);
|
|
lv_obj_t *SF[6] = { id(gy_s0), id(gy_s1), id(gy_s2), id(gy_s3), id(gy_s4), id(gy_s5) };
|
|
for (int k = 0; k < 6; k++) lv_obj_set_pos(SF[k], 400, 400);
|
|
lv_obj_set_pos(id(gy_ship), 400, 400);
|
|
- lvgl.widget.hide: gy_x0
|
|
- lvgl.widget.hide: gy_x1
|
|
- id: gy_start
|
|
then:
|
|
- lambda: |-
|
|
id(g_gy_state) = 1; id(g_gy_score) = 0; id(g_gy_lives) = 3; id(g_gy_dead) = false;
|
|
id(g_gy_ship_ang) = -1.5708f; // start at the top
|
|
id(g_gy_fire_clk) = 0; id(g_gy_spawn_clk) = 0;
|
|
for (int k = 0; k < 6; k++) id(g_gy_bon)[k] = false;
|
|
for (int k = 0; k < 10; k++) id(g_gy_eon)[k] = false;
|
|
for (int k = 0; k < 6; k++) { id(g_gy_sr)[k] = 6.0f + (esp_random() % 168); id(g_gy_sa)[k] = (esp_random() % 62832) / 10000.0f; }
|
|
for (int s = 0; s < 2; s++) id(g_gy_exage)[s] = -1;
|
|
id(g_gy_shipframe) = -1; // force a ship-sprite refresh on the first tick
|
|
id(g_knob_capture) = true; id(g_knob_delta) = 0;
|
|
- script.execute: gy_screen
|
|
- script.execute: gy_hud
|
|
- id: gy_over
|
|
then:
|
|
- lambda: |-
|
|
id(g_gy_state) = 2;
|
|
int s = id(g_gy_score);
|
|
if (s > id(g_gy_best)) id(g_gy_best) = s;
|
|
for (int i = 0; i < 10; i++) {
|
|
if (s > id(g_gy_top)[i]) {
|
|
for (int j = 9; j > i; j--) id(g_gy_top)[j] = id(g_gy_top)[j-1];
|
|
id(g_gy_top)[i] = s;
|
|
break;
|
|
}
|
|
}
|
|
- script.execute: gy_screen
|
|
# screen state + the single knob-capture authority (captured only in play + game over)
|
|
- id: gy_screen
|
|
then:
|
|
- lambda: 'id(g_knob_capture) = (id(g_gy_state) == 1 || id(g_gy_state) == 2);'
|
|
- script.execute: gy_clear
|
|
- lvgl.widget.hide: gy_logo
|
|
- lvgl.widget.hide: gy_ring
|
|
- lvgl.widget.hide: gy_ring2
|
|
- lvgl.widget.hide: gy_ring3
|
|
- lvgl.widget.hide: gy_core
|
|
- lvgl.widget.hide: lbl_gy_score
|
|
- lvgl.widget.hide: lbl_gy_lives
|
|
- lvgl.widget.hide: lbl_gy_title
|
|
- lvgl.widget.hide: lbl_gy_sub
|
|
- lvgl.widget.hide: btn_gy_start
|
|
- lvgl.widget.hide: btn_gy_howto
|
|
- lvgl.widget.hide: btn_gy_scores
|
|
- lvgl.widget.hide: lbl_gy_over
|
|
- lvgl.widget.hide: lbl_gy_rec
|
|
- lvgl.widget.hide: btn_gy_replay
|
|
- lvgl.widget.hide: btn_gy_exit
|
|
- lvgl.widget.hide: lbl_gy_howto_t
|
|
- lvgl.widget.hide: gy_hb1
|
|
- lvgl.widget.hide: gy_hb2
|
|
- lvgl.widget.hide: gy_hb3
|
|
- lvgl.widget.hide: gy_hb4
|
|
- lvgl.widget.hide: lbl_gy_howto1
|
|
- lvgl.widget.hide: lbl_gy_howto2
|
|
- lvgl.widget.hide: lbl_gy_howto3
|
|
- lvgl.widget.hide: lbl_gy_howto4
|
|
- lvgl.widget.hide: gy_life_icon
|
|
- lvgl.widget.hide: lbl_gy_scores_t
|
|
- lvgl.widget.hide: lbl_gy_scores1
|
|
- lvgl.widget.hide: lbl_gy_scores2
|
|
- lvgl.widget.hide: btn_gy_back
|
|
- if: # MENU
|
|
condition: { lambda: 'return id(g_gy_state) == 0;' }
|
|
then:
|
|
- lvgl.widget.show: gy_logo
|
|
- lvgl.widget.show: btn_gy_start
|
|
- lvgl.widget.show: btn_gy_howto
|
|
- lvgl.widget.show: btn_gy_scores
|
|
- if: # PLAY
|
|
condition: { lambda: 'return id(g_gy_state) == 1;' }
|
|
then:
|
|
- lvgl.widget.show: gy_ring
|
|
- lvgl.widget.show: gy_ring2
|
|
- lvgl.widget.show: gy_ring3
|
|
- lvgl.widget.show: gy_core
|
|
- lvgl.widget.show: lbl_gy_score
|
|
- lvgl.widget.show: lbl_gy_lives
|
|
- script.execute: gy_render
|
|
- if: # GAME OVER
|
|
condition: { lambda: 'return id(g_gy_state) == 2;' }
|
|
then:
|
|
- lvgl.widget.show: gy_logo
|
|
- lvgl.widget.show: lbl_gy_over
|
|
- lvgl.widget.show: lbl_gy_rec
|
|
- lvgl.widget.show: btn_gy_replay
|
|
- lvgl.widget.show: btn_gy_exit
|
|
- lvgl.label.update: { id: lbl_gy_over, text: !lambda 'char b[24]; snprintf(b,sizeof(b),"Score: %d", id(g_gy_score)); return std::string(b);' }
|
|
- lvgl.label.update: { id: lbl_gy_rec, text: !lambda 'char b[24]; snprintf(b,sizeof(b),"Best: %d", id(g_gy_best)); return std::string(b);' }
|
|
- if: # HOW TO PLAY
|
|
condition: { lambda: 'return id(g_gy_state) == 3;' }
|
|
then:
|
|
- lvgl.widget.show: lbl_gy_howto_t
|
|
- lvgl.widget.show: gy_hb1
|
|
- lvgl.widget.show: gy_hb2
|
|
- lvgl.widget.show: gy_hb3
|
|
- lvgl.widget.show: gy_hb4
|
|
- lvgl.widget.show: lbl_gy_howto1
|
|
- lvgl.widget.show: lbl_gy_howto2
|
|
- lvgl.widget.show: lbl_gy_howto3
|
|
- lvgl.widget.show: lbl_gy_howto4
|
|
- lvgl.widget.show: gy_life_icon
|
|
- lvgl.widget.show: btn_gy_back
|
|
- if: # SCORES (top 10)
|
|
condition: { lambda: 'return id(g_gy_state) == 4;' }
|
|
then:
|
|
- lvgl.widget.show: lbl_gy_scores_t
|
|
- lvgl.widget.show: lbl_gy_scores1
|
|
- lvgl.widget.show: lbl_gy_scores2
|
|
- lvgl.widget.show: btn_gy_back
|
|
- lvgl.label.update:
|
|
id: lbl_gy_scores1
|
|
text: !lambda |-
|
|
std::string s; char b[24];
|
|
for (int i = 0; i < 5; i++) { if(i) s += "\n"; if(id(g_gy_top)[i]>0){ snprintf(b,sizeof(b),"%d. %d",i+1,id(g_gy_top)[i]); } else { snprintf(b,sizeof(b),"%d. -",i+1); } s += b; }
|
|
return s;
|
|
- lvgl.label.update:
|
|
id: lbl_gy_scores2
|
|
text: !lambda |-
|
|
std::string s; char b[24];
|
|
for (int i = 5; i < 10; i++) { if(i>5) s += "\n"; if(id(g_gy_top)[i]>0){ snprintf(b,sizeof(b),"%d. %d",i+1,id(g_gy_top)[i]); } else { snprintf(b,sizeof(b),"%d. -",i+1); } s += b; }
|
|
return s;
|
|
|
|
interval:
|
|
- interval: 250ms
|
|
then:
|
|
- if:
|
|
condition: { lambda: 'return id(g_gy_reset) || id(g_factory_reset);' }
|
|
then:
|
|
- lambda: 'for (int i = 0; i < 10; i++) id(g_gy_top)[i] = 0; id(g_gy_best) = 0; id(g_gy_reset) = false;'
|
|
- interval: 50ms
|
|
then:
|
|
- if:
|
|
condition: { lambda: 'return id(g_nav_req) && id(g_base)==10;' }
|
|
then:
|
|
- lambda: 'id(g_nav_req) = false; id(g_gy_state) = 0; id(g_knob_delta) = 0;'
|
|
- if: { condition: { lambda: 'return id(g_nav_anim)==0;' }, then: [lvgl.page.show: { id: page_gyruss, animation: move_left, time: 250ms }] }
|
|
- if: { condition: { lambda: 'return id(g_nav_anim)==1;' }, then: [lvgl.page.show: { id: page_gyruss, animation: move_right, time: 250ms }] }
|
|
- if: { condition: { lambda: 'return id(g_nav_anim)==2;' }, then: [lvgl.page.show: { id: page_gyruss, animation: move_top, time: 250ms }] }
|
|
- if: { condition: { lambda: 'return id(g_nav_anim)==3;' }, then: [lvgl.page.show: { id: page_gyruss, animation: move_bottom, time: 250ms }] }
|
|
- script.execute: gy_screen
|
|
# Game tick: aim (knob) + auto-fire + spiral the foes + collisions; all positioning in gy_render.
|
|
- interval: 50ms
|
|
then:
|
|
- if:
|
|
condition: { lambda: 'return id(g_base) == 10 && id(g_gy_state) == 1;' }
|
|
then:
|
|
- lambda: |-
|
|
id(g_gy_hud_dirty) = false; id(g_gy_shipdirty) = false;
|
|
id(g_gy_tick)++;
|
|
int kd = id(g_knob_delta); id(g_knob_delta) = 0;
|
|
id(g_gy_ship_ang) += kd * 0.30f; // aim sensitivity: ~17 deg per knob detent
|
|
// auto-fire a bullet down the ship's radial
|
|
if (++id(g_gy_fire_clk) >= 5) {
|
|
id(g_gy_fire_clk) = 0;
|
|
for (int k = 0; k < 6; k++) if (!id(g_gy_bon)[k]) { id(g_gy_bon)[k] = true; id(g_gy_br)[k] = 138.0f; id(g_gy_ba)[k] = id(g_gy_ship_ang); break; }
|
|
}
|
|
for (int k = 0; k < 6; k++) if (id(g_gy_bon)[k]) { id(g_gy_br)[k] -= 9.0f; if (id(g_gy_br)[k] < 12.0f) id(g_gy_bon)[k] = false; }
|
|
// spawn foes from the core (faster as the score climbs)
|
|
int sw = 24 - id(g_gy_score) / 200; if (sw < 9) sw = 9;
|
|
if (++id(g_gy_spawn_clk) >= sw) {
|
|
id(g_gy_spawn_clk) = 0;
|
|
for (int k = 0; k < 10; k++) if (!id(g_gy_eon)[k]) {
|
|
float a = (esp_random() % 62832) / 10000.0f;
|
|
id(g_gy_eon)[k] = true; id(g_gy_er)[k] = 32.0f; id(g_gy_ea)[k] = a; break; // start at the core's rim (not under it) to avoid central overdraw
|
|
}
|
|
}
|
|
// age existing explosions first (8-frame sprite); refresh sprite while any is live/expiring
|
|
id(g_gy_exact) = false;
|
|
for (int s = 0; s < 2; s++) if (id(g_gy_exage)[s] >= 0) {
|
|
id(g_gy_exact) = true;
|
|
if (++id(g_gy_exage)[s] > 7) id(g_gy_exage)[s] = -1;
|
|
}
|
|
// foes spiral outward; ANY foe that reaches the rim costs a life and explodes there
|
|
float esp = 1.5f + id(g_gy_score) / 1500.0f; if (esp > 3.2f) esp = 3.2f;
|
|
for (int k = 0; k < 10; k++) if (id(g_gy_eon)[k]) {
|
|
id(g_gy_er)[k] += esp; id(g_gy_ea)[k] += 0.05f;
|
|
if (id(g_gy_er)[k] >= 146.0f) {
|
|
id(g_gy_eon)[k] = false;
|
|
id(g_gy_lives)--; id(g_gy_hud_dirty) = true; if (id(g_gy_lives) <= 0) id(g_gy_dead) = true;
|
|
for (int s = 0; s < 2; s++) if (id(g_gy_exage)[s] < 0) { id(g_gy_exx)[s] = cosf(id(g_gy_ea)[k]) * 146.0f; id(g_gy_exy)[s] = sinf(id(g_gy_ea)[k]) * 146.0f; id(g_gy_exage)[s] = 0; id(g_gy_exact) = true; break; }
|
|
}
|
|
}
|
|
// depth starfield (throttled to every 2nd tick): rush outward, respawn near the centre
|
|
if ((id(g_gy_tick) & 1) == 0) for (int k = 0; k < 6; k++) {
|
|
id(g_gy_sr)[k] += id(g_gy_sr)[k] * 0.10f + 2.0f;
|
|
if (id(g_gy_sr)[k] > 176.0f) { id(g_gy_sr)[k] = 4.0f + (esp_random() % 14); id(g_gy_sa)[k] = (esp_random() % 62832) / 10000.0f; }
|
|
}
|
|
// ship rotation frame (every 2nd tick, on the ODD ticks so it doesn't share a tick with
|
|
// the starfield move above - spreads the two throttled costs; position stays 50ms)
|
|
if ((id(g_gy_tick) & 1) == 1) {
|
|
int bk = (int) lroundf(id(g_gy_ship_ang) / 0.3926991f); bk %= 16; if (bk < 0) bk += 16;
|
|
id(g_gy_shipdirty) = (bk != id(g_gy_shipframe));
|
|
if (id(g_gy_shipdirty)) id(g_gy_shipframe) = bk;
|
|
}
|
|
// bullet vs foe (polar proximity)
|
|
for (int b = 0; b < 6; b++) if (id(g_gy_bon)[b]) for (int e = 0; e < 10; e++) if (id(g_gy_eon)[e]) {
|
|
float dr = id(g_gy_br)[b] - id(g_gy_er)[e]; if (dr < 0) dr = -dr;
|
|
if (dr < 13.0f) {
|
|
float da = id(g_gy_ba)[b] - id(g_gy_ea)[e]; da = atan2f(sinf(da), cosf(da));
|
|
if (fabsf(da) < 0.28f) { id(g_gy_bon)[b] = false; id(g_gy_eon)[e] = false; id(g_gy_score) += 10; id(g_gy_hud_dirty) = true; break; }
|
|
}
|
|
}
|
|
- if: { condition: { lambda: 'return id(g_gy_shipdirty);' }, then: [ script.execute: gy_shipspr ] }
|
|
- if: { condition: { lambda: 'return id(g_gy_exact);' }, then: [ script.execute: gy_explspr ] }
|
|
- script.execute: gy_render
|
|
- if:
|
|
condition: { lambda: 'return id(g_gy_dead);' }
|
|
then:
|
|
- script.execute: gy_over
|
|
else:
|
|
- if:
|
|
condition: { lambda: 'return id(g_gy_hud_dirty);' }
|
|
then:
|
|
- script.execute: gy_hud
|
|
|
|
# Sprites are pulled from the repo /main/ raw URLs at compile time.
|
|
# Third-party art (see assets/sprites/knobuss/CREDITS.txt).
|
|
image:
|
|
- id: img_gy_ship0
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship0.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship1
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship1.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship2
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship2.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship3
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship3.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship4
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship4.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship5
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship5.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship6
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship6.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship7
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship7.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship8
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship8.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship9
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship9.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship10
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship10.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship11
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship11.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship12
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship12.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship13
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship13.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship14
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship14.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_ship15
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/ship15.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_enemy0
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/enemy0.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_enemy1
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/enemy1.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_enemy2
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/enemy2.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_enemy3
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/enemy3.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_expl0
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/expl0.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_expl1
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/expl1.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_expl2
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/expl2.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_expl3
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/expl3.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_expl4
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/expl4.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_expl5
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/expl5.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_expl6
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/expl6.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_expl7
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/expl7.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_bullet
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/bullet.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_core
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/core.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
- id: img_gy_logo
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/knobuss-logo.png"
|
|
type: RGB565
|
|
- id: img_gy_life
|
|
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/knobuss/life.png"
|
|
type: RGB565
|
|
transparency: alpha_channel
|
|
lvgl:
|
|
pages:
|
|
- id: page_gyruss
|
|
bg_color: 0x070611
|
|
scrollbar_mode: "off"
|
|
scrollable: false
|
|
widgets:
|
|
# Knobuss title screen (full-screen menu backdrop, behind the buttons) - hidden in play
|
|
- image: { id: gy_logo, align: CENTER, x: 0, y: 0, src: img_gy_logo, hidden: true }
|
|
# starfield (declared first = drawn behind everything) - parked until play
|
|
- obj: { id: gy_s0, x: 400, y: 400, width: 3, height: 3, radius: 999, bg_color: 0x6076A4, bg_opa: COVER, border_width: 0, pad_all: 0 }
|
|
- obj: { id: gy_s1, x: 400, y: 400, width: 2, height: 2, radius: 999, bg_color: 0x32466E, bg_opa: COVER, border_width: 0, pad_all: 0 }
|
|
- obj: { id: gy_s2, x: 400, y: 400, width: 3, height: 3, radius: 999, bg_color: 0x6076A4, bg_opa: COVER, border_width: 0, pad_all: 0 }
|
|
- obj: { id: gy_s3, x: 400, y: 400, width: 2, height: 2, radius: 999, bg_color: 0x32466E, bg_opa: COVER, border_width: 0, pad_all: 0 }
|
|
- obj: { id: gy_s4, x: 400, y: 400, width: 3, height: 3, radius: 999, bg_color: 0x6076A4, bg_opa: COVER, border_width: 0, pad_all: 0 }
|
|
- obj: { id: gy_s5, x: 400, y: 400, width: 2, height: 2, radius: 999, bg_color: 0x32466E, bg_opa: COVER, border_width: 0, pad_all: 0 }
|
|
# concentric guide rings (static) suggest a tube receding to the core - free depth cue
|
|
- obj: { id: gy_ring3, align: CENTER, x: 0, y: 0, width: 112, height: 112, radius: 56, bg_opa: TRANSP, border_width: 1, border_color: 0x1A2742, hidden: true }
|
|
- obj: { id: gy_ring2, align: CENTER, x: 0, y: 0, width: 200, height: 200, radius: 100, bg_opa: TRANSP, border_width: 1, border_color: 0x1E2C4A, hidden: true }
|
|
- obj: { id: gy_ring, align: CENTER, x: 0, y: 0, width: 300, height: 300, radius: 150, bg_opa: TRANSP, border_width: 2, border_color: 0x2A3C66, hidden: true }
|
|
- image: { id: gy_core, align: CENTER, x: 0, y: 0, src: img_gy_core, hidden: true }
|
|
# foes: 4 glowing-orb colours cycled across the 10 slots (fixed src - no runtime swap)
|
|
- image: { id: gy_e0, x: 400, y: 400, src: img_gy_enemy0 }
|
|
- image: { id: gy_e1, x: 400, y: 400, src: img_gy_enemy1 }
|
|
- image: { id: gy_e2, x: 400, y: 400, src: img_gy_enemy2 }
|
|
- image: { id: gy_e3, x: 400, y: 400, src: img_gy_enemy3 }
|
|
- image: { id: gy_e4, x: 400, y: 400, src: img_gy_enemy0 }
|
|
- image: { id: gy_e5, x: 400, y: 400, src: img_gy_enemy1 }
|
|
- image: { id: gy_e6, x: 400, y: 400, src: img_gy_enemy2 }
|
|
- image: { id: gy_e7, x: 400, y: 400, src: img_gy_enemy3 }
|
|
- image: { id: gy_e8, x: 400, y: 400, src: img_gy_enemy0 }
|
|
- image: { id: gy_e9, x: 400, y: 400, src: img_gy_enemy1 }
|
|
- image: { id: gy_b0, x: 400, y: 400, src: img_gy_bullet }
|
|
- image: { id: gy_b1, x: 400, y: 400, src: img_gy_bullet }
|
|
- image: { id: gy_b2, x: 400, y: 400, src: img_gy_bullet }
|
|
- image: { id: gy_b3, x: 400, y: 400, src: img_gy_bullet }
|
|
- image: { id: gy_b4, x: 400, y: 400, src: img_gy_bullet }
|
|
- image: { id: gy_b5, x: 400, y: 400, src: img_gy_bullet }
|
|
- image: { id: gy_ship, x: 400, y: 400, src: img_gy_ship0 }
|
|
# explosion sprites (8-frame), parked + hidden until a foe hits the rim
|
|
- image: { id: gy_x0, x: 400, y: 400, src: img_gy_expl0, hidden: true }
|
|
- image: { id: gy_x1, x: 400, y: 400, src: img_gy_expl0, hidden: true }
|
|
- label: { id: lbl_gy_score, align: CENTER, x: 0, y: -150, text: "0", text_font: font_med, text_color: 0xCFE4FF, hidden: true }
|
|
- label: { id: lbl_gy_lives, align: CENTER, x: 0, y: 150, text: "LIVES 3", text_font: font_artist, text_color: 0xFF7A6A, hidden: true }
|
|
- label: { id: lbl_gy_title, align: CENTER, x: 0, y: -86, text: "KNOBUSS", text_font: font_sel, text_color: 0x46E3D0, hidden: true }
|
|
- label: { id: lbl_gy_sub, align: CENTER, x: 0, y: -52, text: "tube shooter", text_font: font_artist, text_color: 0x6B7BA6, hidden: true }
|
|
- button:
|
|
id: btn_gy_start
|
|
align: CENTER
|
|
y: -8
|
|
width: 150
|
|
height: 44
|
|
radius: 22
|
|
bg_color: 0x39C6E6
|
|
bg_opa: COVER
|
|
border_width: 0
|
|
on_click: [ script.execute: gy_start ]
|
|
widgets:
|
|
- label: { align: CENTER, text: "Start", text_font: font_med, text_color: 0x06131A }
|
|
- button:
|
|
id: btn_gy_howto
|
|
align: CENTER
|
|
y: 40
|
|
width: 150
|
|
height: 44
|
|
radius: 22
|
|
bg_color: 0x1C2A44
|
|
bg_opa: COVER
|
|
border_width: 0
|
|
on_click: [ lambda: 'id(g_gy_state) = 3;', script.execute: gy_screen ]
|
|
widgets:
|
|
- label: { align: CENTER, text: "How to play?", text_font: font_med, text_color: 0xC4D2F0 }
|
|
- button:
|
|
id: btn_gy_scores
|
|
align: CENTER
|
|
y: 88
|
|
width: 150
|
|
height: 44
|
|
radius: 22
|
|
bg_color: 0x1C2A44
|
|
bg_opa: COVER
|
|
border_width: 0
|
|
on_click: [ lambda: 'id(g_gy_state) = 4;', script.execute: gy_screen ]
|
|
widgets:
|
|
- label: { align: CENTER, text: "Scores", text_font: font_med, text_color: 0xC4D2F0 }
|
|
- label: { id: lbl_gy_over, align: CENTER, x: 0, y: -10, text_align: CENTER, text: "", text_font: font_sel, text_color: 0xCFE4FF, hidden: true }
|
|
- label: { id: lbl_gy_rec, align: CENTER, x: 0, y: 22, text_align: CENTER, text: "", text_font: font_artist, text_color: 0x6B7BA6, hidden: true }
|
|
- button:
|
|
id: btn_gy_replay
|
|
align: CENTER
|
|
x: -66
|
|
y: 62
|
|
width: 112
|
|
height: 48
|
|
radius: 24
|
|
bg_color: 0x39C6E6
|
|
bg_opa: COVER
|
|
border_width: 0
|
|
hidden: true
|
|
on_click: [ script.execute: gy_start ]
|
|
widgets:
|
|
- label: { align: CENTER, text: "Play", text_font: font_med, text_color: 0x06131A }
|
|
- button:
|
|
id: btn_gy_exit
|
|
align: CENTER
|
|
x: 66
|
|
y: 62
|
|
width: 112
|
|
height: 48
|
|
radius: 24
|
|
bg_color: 0x2A2E3D
|
|
bg_opa: COVER
|
|
border_width: 0
|
|
hidden: true
|
|
on_click: [ lambda: 'id(g_gy_state) = 0;', script.execute: gy_screen ]
|
|
widgets:
|
|
- label: { align: CENTER, text: "Exit", text_font: font_med, text_color: 0xFFFFFF }
|
|
- label: { id: lbl_gy_howto_t, align: CENTER, y: -104, text: "HOW TO PLAY?", text_font: font_sel, text_color: 0x46E3D0, hidden: true }
|
|
- obj: { id: gy_hb1, align: CENTER, x: -96, y: -52, width: 24, height: 24, radius: 12, bg_color: 0x2C6FB2, bg_opa: COVER, border_width: 0, pad_all: 0, hidden: true, widgets: [ label: { align: CENTER, text: "1", text_font: font_artist, text_color: 0xFFFFFF } ] }
|
|
- obj: { id: gy_hb2, align: CENTER, x: -96, y: -16, width: 24, height: 24, radius: 12, bg_color: 0x2C6FB2, bg_opa: COVER, border_width: 0, pad_all: 0, hidden: true, widgets: [ label: { align: CENTER, text: "2", text_font: font_artist, text_color: 0xFFFFFF } ] }
|
|
- obj: { id: gy_hb3, align: CENTER, x: -96, y: 20, width: 24, height: 24, radius: 12, bg_color: 0x2C6FB2, bg_opa: COVER, border_width: 0, pad_all: 0, hidden: true, widgets: [ label: { align: CENTER, text: "3", text_font: font_artist, text_color: 0xFFFFFF } ] }
|
|
- obj: { id: gy_hb4, align: CENTER, x: -96, y: 56, width: 24, height: 24, radius: 12, bg_color: 0x2C6FB2, bg_opa: COVER, border_width: 0, pad_all: 0, hidden: true, widgets: [ label: { align: CENTER, text: "4", text_font: font_artist, text_color: 0xFFFFFF } ] }
|
|
- label: { id: lbl_gy_howto1, align_to: { id: gy_hb1, align: OUT_RIGHT_MID, x: 8 }, text: "Turn knob to aim", text_font: font_med, text_color: 0xC4D2F0, hidden: true }
|
|
- label: { id: lbl_gy_howto2, align_to: { id: gy_hb2, align: OUT_RIGHT_MID, x: 8 }, text: "You auto-fire inward", text_font: font_med, text_color: 0xC4D2F0, hidden: true }
|
|
- label: { id: lbl_gy_howto3, align_to: { id: gy_hb3, align: OUT_RIGHT_MID, x: 8 }, text: "Blast foes before the rim", text_font: font_med, text_color: 0xC4D2F0, hidden: true }
|
|
- label: { id: lbl_gy_howto4, align_to: { id: gy_hb4, align: OUT_RIGHT_MID, x: 8 }, text: "You have three lives", text_font: font_med, text_color: 0xC4D2F0, hidden: true }
|
|
- image: { id: gy_life_icon, align_to: { id: lbl_gy_howto4, align: OUT_RIGHT_MID, x: 8 }, src: img_gy_life, hidden: true }
|
|
- label: { id: lbl_gy_scores_t, align: CENTER, y: -116, text: "SCORES", text_font: font_sel, text_color: 0x46E3D0, hidden: true }
|
|
- label: { id: lbl_gy_scores1, align: CENTER, x: -52, y: 0, text: "", text_align: LEFT, text_font: font_artist, text_color: 0xC4D2F0, hidden: true }
|
|
- label: { id: lbl_gy_scores2, align: CENTER, x: 56, y: 0, text: "", text_align: LEFT, text_font: font_artist, text_color: 0xC4D2F0, hidden: true }
|
|
- button:
|
|
id: btn_gy_back
|
|
align: CENTER
|
|
y: 120
|
|
width: 130
|
|
height: 42
|
|
radius: 21
|
|
bg_color: 0x2A2E3D
|
|
bg_opa: COVER
|
|
border_width: 0
|
|
hidden: true
|
|
on_click: [ lambda: 'id(g_gy_state) = 0;', script.execute: gy_screen ]
|
|
widgets:
|
|
- label: { align: CENTER, text: "Back", text_font: font_med, text_color: 0xFFFFFF }
|