@ludra/cli 0.0.0-stage → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +37 -2
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.js +21 -0
- package/dist/commands/components.d.ts +3 -0
- package/dist/commands/components.js +55 -0
- package/dist/commands/dev.d.ts +4 -0
- package/dist/commands/dev.js +67 -0
- package/dist/commands/new.d.ts +3 -0
- package/dist/commands/new.js +46 -0
- package/dist/commands/play.d.ts +12 -0
- package/dist/commands/play.js +126 -0
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.js +55 -0
- package/dist/format.d.ts +11 -0
- package/dist/format.js +24 -0
- package/dist/headless.d.ts +16 -0
- package/dist/headless.js +202 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +80 -0
- package/dist/project.d.ts +29 -0
- package/dist/project.js +94 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.js +93 -0
- package/package.json +49 -4
- package/templates/platformer/CLAUDE.md +30 -0
- package/templates/platformer/_gitignore +2 -0
- package/templates/platformer/assets/coin.svg +22 -0
- package/templates/platformer/assets/flag.svg +15 -0
- package/templates/platformer/assets/ground.svg +12 -0
- package/templates/platformer/assets/lift.svg +9 -0
- package/templates/platformer/assets/platform.svg +9 -0
- package/templates/platformer/assets/player.svg +65 -0
- package/templates/platformer/assets/slime.svg +21 -0
- package/templates/platformer/assets/spikes.svg +8 -0
- package/templates/platformer/ludra.json +5 -0
- package/templates/platformer/package.json +13 -0
- package/templates/platformer/scenes/main.scene.json +154 -0
- package/templates/platformer/tests/gameplay.test.json +36 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
|
2
|
+
<!-- Tile: riveted metal with hazard stripes for moving platforms. -->
|
|
3
|
+
<rect width="20" height="20" fill="#8fa3d9"/>
|
|
4
|
+
<rect width="20" height="3" fill="#c3d0f2"/>
|
|
5
|
+
<rect y="14" width="20" height="6" fill="#f2c14e"/>
|
|
6
|
+
<path d="M0 20 L 6 14 L 10 14 L 4 20 Z M10 20 L 16 14 L 20 14 L 14 20 Z" fill="#2b3150"/>
|
|
7
|
+
<circle cx="5" cy="8" r="1.4" fill="#5d6f9e"/>
|
|
8
|
+
<circle cx="15" cy="8" r="1.4" fill="#5d6f9e"/>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
|
2
|
+
<!-- Tile: stone brick for ledges and steps. -->
|
|
3
|
+
<rect width="20" height="20" fill="#56658f"/>
|
|
4
|
+
<rect width="20" height="3" fill="#7a8ab8"/>
|
|
5
|
+
<rect y="17" width="20" height="3" fill="#434f72"/>
|
|
6
|
+
<rect x="9" y="3" width="2" height="14" fill="#434f72"/>
|
|
7
|
+
<rect x="1" y="5" width="3" height="2" fill="#6b7aa6"/>
|
|
8
|
+
<rect x="13" y="9" width="4" height="2" fill="#6b7aa6"/>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="140" height="36" viewBox="0 0 140 36">
|
|
2
|
+
<!-- Spritesheet: 5 frames of 28x36, facing right. 0 idle, 1-2 run, 3 jump, 4 fall. -->
|
|
3
|
+
<defs>
|
|
4
|
+
<g id="eyes">
|
|
5
|
+
<ellipse cx="15" cy="15" rx="3.2" ry="3.8" fill="#ffffff"/>
|
|
6
|
+
<ellipse cx="21.5" cy="15" rx="3.2" ry="3.8" fill="#ffffff"/>
|
|
7
|
+
<circle cx="16.3" cy="15.6" r="1.7" fill="#1d2333"/>
|
|
8
|
+
<circle cx="22.8" cy="15.6" r="1.7" fill="#1d2333"/>
|
|
9
|
+
</g>
|
|
10
|
+
<path id="leaf" d="M14 7 q 2 -6 8 -5 q -2 5 -8 5 z" fill="#4cc9a0"/>
|
|
11
|
+
</defs>
|
|
12
|
+
|
|
13
|
+
<!-- 0: idle -->
|
|
14
|
+
<g>
|
|
15
|
+
<rect x="7" y="29" width="6" height="6" rx="2" fill="#3f9e5c"/>
|
|
16
|
+
<rect x="16" y="29" width="6" height="6" rx="2" fill="#3f9e5c"/>
|
|
17
|
+
<rect x="3" y="7" width="22" height="24" rx="8" fill="#7cf29c"/>
|
|
18
|
+
<rect x="3" y="21" width="22" height="10" rx="5" fill="#62d886"/>
|
|
19
|
+
<use href="#leaf"/>
|
|
20
|
+
<use href="#eyes"/>
|
|
21
|
+
</g>
|
|
22
|
+
|
|
23
|
+
<!-- 1: run, legs apart -->
|
|
24
|
+
<g transform="translate(28 0)">
|
|
25
|
+
<rect x="3" y="28" width="6" height="6" rx="2" fill="#3f9e5c"/>
|
|
26
|
+
<rect x="19" y="29" width="6" height="6" rx="2" fill="#3f9e5c"/>
|
|
27
|
+
<rect x="3" y="6" width="22" height="24" rx="8" fill="#7cf29c"/>
|
|
28
|
+
<rect x="3" y="20" width="22" height="10" rx="5" fill="#62d886"/>
|
|
29
|
+
<use href="#leaf" transform="translate(0 -1)"/>
|
|
30
|
+
<use href="#eyes" transform="translate(0 -1)"/>
|
|
31
|
+
</g>
|
|
32
|
+
|
|
33
|
+
<!-- 2: run, legs together -->
|
|
34
|
+
<g transform="translate(56 0)">
|
|
35
|
+
<rect x="10" y="29" width="6" height="6" rx="2" fill="#3f9e5c"/>
|
|
36
|
+
<rect x="14" y="28" width="6" height="6" rx="2" fill="#3f9e5c"/>
|
|
37
|
+
<rect x="3" y="7" width="22" height="24" rx="8" fill="#7cf29c"/>
|
|
38
|
+
<rect x="3" y="21" width="22" height="10" rx="5" fill="#62d886"/>
|
|
39
|
+
<use href="#leaf"/>
|
|
40
|
+
<use href="#eyes"/>
|
|
41
|
+
</g>
|
|
42
|
+
|
|
43
|
+
<!-- 3: jump, stretched -->
|
|
44
|
+
<g transform="translate(84 0)">
|
|
45
|
+
<rect x="8" y="28" width="5" height="7" rx="2" fill="#3f9e5c"/>
|
|
46
|
+
<rect x="15" y="28" width="5" height="7" rx="2" fill="#3f9e5c"/>
|
|
47
|
+
<rect x="4" y="3" width="20" height="27" rx="8" fill="#7cf29c"/>
|
|
48
|
+
<rect x="4" y="19" width="20" height="11" rx="5" fill="#62d886"/>
|
|
49
|
+
<use href="#leaf" transform="translate(0 -4)"/>
|
|
50
|
+
<use href="#eyes" transform="translate(-0.5 -3)"/>
|
|
51
|
+
</g>
|
|
52
|
+
|
|
53
|
+
<!-- 4: fall, squashed, looking down -->
|
|
54
|
+
<g transform="translate(112 0)">
|
|
55
|
+
<rect x="4" y="29" width="7" height="6" rx="2" fill="#3f9e5c"/>
|
|
56
|
+
<rect x="17" y="29" width="7" height="6" rx="2" fill="#3f9e5c"/>
|
|
57
|
+
<rect x="1" y="10" width="26" height="21" rx="8" fill="#7cf29c"/>
|
|
58
|
+
<rect x="1" y="22" width="26" height="9" rx="4.5" fill="#62d886"/>
|
|
59
|
+
<use href="#leaf" transform="translate(0 3)"/>
|
|
60
|
+
<ellipse cx="15" cy="18" rx="3.2" ry="3.8" fill="#ffffff"/>
|
|
61
|
+
<ellipse cx="21.5" cy="18" rx="3.2" ry="3.8" fill="#ffffff"/>
|
|
62
|
+
<circle cx="16" cy="20" r="1.7" fill="#1d2333"/>
|
|
63
|
+
<circle cx="22.5" cy="20" r="1.7" fill="#1d2333"/>
|
|
64
|
+
</g>
|
|
65
|
+
</svg>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="26" viewBox="0 0 64 26">
|
|
2
|
+
<!-- Spritesheet: 2 frames of 32x26, a bouncing slime facing right. -->
|
|
3
|
+
<g>
|
|
4
|
+
<path d="M2 25 C 2 10, 8 3, 16 3 C 24 3, 30 10, 30 25 Z" fill="#c77dff"/>
|
|
5
|
+
<path d="M5 25 C 5 18, 9 14, 13 14 C 9 17, 8 21, 8 25 Z" fill="#e0b0ff" opacity="0.7"/>
|
|
6
|
+
<path d="M14 11 l 6 2" stroke="#2a1440" stroke-width="2" stroke-linecap="round"/>
|
|
7
|
+
<path d="M27 11 l -4 2" stroke="#2a1440" stroke-width="2" stroke-linecap="round"/>
|
|
8
|
+
<circle cx="18" cy="15" r="2" fill="#2a1440"/>
|
|
9
|
+
<circle cx="24" cy="15" r="2" fill="#2a1440"/>
|
|
10
|
+
<path d="M17 20 q 4 2 8 0" stroke="#2a1440" stroke-width="1.5" fill="none" stroke-linecap="round"/>
|
|
11
|
+
</g>
|
|
12
|
+
<g transform="translate(32 0)">
|
|
13
|
+
<path d="M0 25 C 0 14, 7 8, 16 8 C 25 8, 32 14, 32 25 Z" fill="#c77dff"/>
|
|
14
|
+
<path d="M3 25 C 3 20, 7 17, 11 17 C 7 19, 6 22, 6 25 Z" fill="#e0b0ff" opacity="0.7"/>
|
|
15
|
+
<path d="M14 15 l 6 2" stroke="#2a1440" stroke-width="2" stroke-linecap="round"/>
|
|
16
|
+
<path d="M28 15 l -4 2" stroke="#2a1440" stroke-width="2" stroke-linecap="round"/>
|
|
17
|
+
<circle cx="18" cy="18.5" r="2" fill="#2a1440"/>
|
|
18
|
+
<circle cx="24.5" cy="18.5" r="2" fill="#2a1440"/>
|
|
19
|
+
<path d="M17 22.5 q 4 1.5 8 0" stroke="#2a1440" stroke-width="1.5" fill="none" stroke-linecap="round"/>
|
|
20
|
+
</g>
|
|
21
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 20 16">
|
|
2
|
+
<!-- Tile: repeats horizontally across the hazard. -->
|
|
3
|
+
<path d="M0 16 L 5 1 L 10 16 Z" fill="#e4e8f2"/>
|
|
4
|
+
<path d="M5 1 L 10 16 L 6.5 16 Z" fill="#a9b1c7"/>
|
|
5
|
+
<path d="M10 16 L 15 1 L 20 16 Z" fill="#e4e8f2"/>
|
|
6
|
+
<path d="M15 1 L 20 16 L 16.5 16 Z" fill="#a9b1c7"/>
|
|
7
|
+
<rect x="0" y="13" width="20" height="3" fill="#ff5d73"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Level 1",
|
|
3
|
+
"world": { "width": 1920, "height": 540, "gravity": 1800, "background": "#1b2140" },
|
|
4
|
+
"effects": {
|
|
5
|
+
"jump": { "sound": "jump", "particles": { "count": 6, "color": "#cfd8ff", "speed": 60, "direction": 90, "spread": 150, "life": 0.25, "size": 3, "gravity": 0 } },
|
|
6
|
+
"land": { "sound": "thud", "particles": { "count": 5, "color": "#cfd8ff", "speed": 50, "direction": -90, "spread": 160, "life": 0.2, "size": 2, "gravity": 300 } },
|
|
7
|
+
"collect": { "sound": "coin", "particles": { "count": 14, "color": "#ffd166", "speed": 140, "life": 0.45, "size": 3, "gravity": 200 } },
|
|
8
|
+
"stomp": { "sound": "hit", "shake": 5, "particles": { "count": 18, "color": "#c77dff", "speed": 180, "life": 0.5, "size": 4 } },
|
|
9
|
+
"die": { "sound": "explosion", "shake": 10, "particles": { "count": 30, "color": "#ff5d73", "speed": 220, "life": 0.7, "size": 4 } },
|
|
10
|
+
"win": { "sound": "powerup", "shake": 3, "particles": { "count": 60, "color": "#4cc9f0", "speed": 260, "life": 1.2, "size": 4, "gravity": 150 } }
|
|
11
|
+
},
|
|
12
|
+
"entities": [
|
|
13
|
+
{
|
|
14
|
+
"id": "player",
|
|
15
|
+
"components": {
|
|
16
|
+
"transform": { "x": 80, "y": 482 },
|
|
17
|
+
"body": { "type": "dynamic", "width": 28, "height": 36 },
|
|
18
|
+
"sprite": {
|
|
19
|
+
"color": "#7cf29c",
|
|
20
|
+
"image": "assets/player.svg",
|
|
21
|
+
"frameWidth": 28,
|
|
22
|
+
"frameHeight": 36,
|
|
23
|
+
"idle": "0",
|
|
24
|
+
"run": "1-2",
|
|
25
|
+
"jump": "3",
|
|
26
|
+
"fall": "4",
|
|
27
|
+
"fps": 10,
|
|
28
|
+
"layer": 3
|
|
29
|
+
},
|
|
30
|
+
"platformer": { "speed": 240, "jumpSpeed": 680 },
|
|
31
|
+
"camera": {}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "ground-start",
|
|
36
|
+
"components": {
|
|
37
|
+
"transform": { "x": 380, "y": 520 },
|
|
38
|
+
"body": { "type": "static", "width": 760, "height": 40 },
|
|
39
|
+
"sprite": { "color": "#3b4766", "image": "assets/ground.svg", "tile": true }
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "ground-end",
|
|
44
|
+
"components": {
|
|
45
|
+
"transform": { "x": 1450, "y": 520 },
|
|
46
|
+
"body": { "type": "static", "width": 940, "height": 40 },
|
|
47
|
+
"sprite": { "color": "#3b4766", "image": "assets/ground.svg", "tile": true }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "wall-left",
|
|
52
|
+
"components": {
|
|
53
|
+
"transform": { "x": -10, "y": 270 },
|
|
54
|
+
"body": { "type": "static", "width": 20, "height": 540 }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "wall-right",
|
|
59
|
+
"components": {
|
|
60
|
+
"transform": { "x": 1930, "y": 270 },
|
|
61
|
+
"body": { "type": "static", "width": 20, "height": 540 }
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "ledge",
|
|
66
|
+
"components": {
|
|
67
|
+
"transform": { "x": 300, "y": 410 },
|
|
68
|
+
"body": { "type": "static", "width": 160, "height": 20 },
|
|
69
|
+
"sprite": { "color": "#56658f", "image": "assets/platform.svg", "tile": true }
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "coin-1",
|
|
74
|
+
"components": {
|
|
75
|
+
"transform": { "x": 300, "y": 370 },
|
|
76
|
+
"body": { "type": "static", "width": 20, "height": 20, "sensor": true },
|
|
77
|
+
"sprite": { "shape": "circle", "color": "#ffd166", "image": "assets/coin.svg", "frameWidth": 20, "idle": "0-3", "layer": 1 },
|
|
78
|
+
"collectible": { "score": 1 }
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "spikes",
|
|
83
|
+
"components": {
|
|
84
|
+
"transform": { "x": 560, "y": 492 },
|
|
85
|
+
"body": { "type": "static", "width": 80, "height": 16, "sensor": true },
|
|
86
|
+
"sprite": { "color": "#ff5d73", "image": "assets/spikes.svg", "tile": true, "layer": 1 },
|
|
87
|
+
"hazard": {}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "lift",
|
|
92
|
+
"components": {
|
|
93
|
+
"transform": { "x": 800, "y": 440 },
|
|
94
|
+
"body": { "type": "kinematic", "width": 120, "height": 20 },
|
|
95
|
+
"sprite": { "color": "#8fa3d9", "image": "assets/lift.svg", "tile": true },
|
|
96
|
+
"patrol": { "axis": "x", "distance": 140, "speed": 90 }
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "coin-2",
|
|
101
|
+
"components": {
|
|
102
|
+
"transform": { "x": 870, "y": 390 },
|
|
103
|
+
"body": { "type": "static", "width": 20, "height": 20, "sensor": true },
|
|
104
|
+
"sprite": { "shape": "circle", "color": "#ffd166", "image": "assets/coin.svg", "frameWidth": 20, "idle": "0-3", "layer": 1 },
|
|
105
|
+
"collectible": { "score": 1 }
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "slime",
|
|
110
|
+
"components": {
|
|
111
|
+
"transform": { "x": 1150, "y": 487 },
|
|
112
|
+
"body": { "type": "kinematic", "width": 32, "height": 26, "sensor": true },
|
|
113
|
+
"sprite": { "color": "#c77dff", "image": "assets/slime.svg", "frameWidth": 32, "idle": "0", "run": "0-1", "fps": 4, "layer": 2 },
|
|
114
|
+
"patrol": { "axis": "x", "distance": 200, "speed": 70 },
|
|
115
|
+
"hazard": { "stompable": true, "score": 1 }
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"id": "step-1",
|
|
120
|
+
"components": {
|
|
121
|
+
"transform": { "x": 1500, "y": 410 },
|
|
122
|
+
"body": { "type": "static", "width": 140, "height": 20 },
|
|
123
|
+
"sprite": { "color": "#56658f", "image": "assets/platform.svg", "tile": true }
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "step-2",
|
|
128
|
+
"components": {
|
|
129
|
+
"transform": { "x": 1680, "y": 330 },
|
|
130
|
+
"body": { "type": "static", "width": 140, "height": 20 },
|
|
131
|
+
"sprite": { "color": "#56658f", "image": "assets/platform.svg", "tile": true }
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "coin-3",
|
|
136
|
+
"components": {
|
|
137
|
+
"transform": { "x": 1680, "y": 290 },
|
|
138
|
+
"body": { "type": "static", "width": 20, "height": 20, "sensor": true },
|
|
139
|
+
"sprite": { "shape": "circle", "color": "#ffd166", "image": "assets/coin.svg", "frameWidth": 20, "idle": "0-3", "layer": 1 },
|
|
140
|
+
"collectible": { "score": 1 }
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"id": "flag",
|
|
145
|
+
"components": {
|
|
146
|
+
"transform": { "x": 1860, "y": 460 },
|
|
147
|
+
"body": { "type": "static", "width": 24, "height": 80, "sensor": true },
|
|
148
|
+
"sprite": { "color": "#4cc9f0", "image": "assets/flag.svg", "frameWidth": 24, "idle": "0-1", "fps": 3, "flip": false, "layer": 1 },
|
|
149
|
+
"emitter": { "rate": 6, "color": "#fff3b0", "speed": 30, "spread": 70, "life": 1.2, "size": 3, "gravity": -20 },
|
|
150
|
+
"goal": { "requireScore": 4 }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tests": [
|
|
3
|
+
{
|
|
4
|
+
"name": "player rests on the ground",
|
|
5
|
+
"duration": "1s",
|
|
6
|
+
"assert": ["player.grounded == true", "player.y == 482", "deaths == 0"]
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "player runs right",
|
|
10
|
+
"inputs": "right:1s",
|
|
11
|
+
"assert": ["player.x > 300 at 1s", "player.grounded == true at 1s"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "jumping leaves the ground and lands again",
|
|
15
|
+
"inputs": "wait:0.2s, jump:0.4s",
|
|
16
|
+
"assert": ["events.jump == 1", "player.y < 400 at 0.5s", "player.grounded == true at end"]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "spikes kill the player, who respawns at the start",
|
|
20
|
+
"inputs": "right:1.85s",
|
|
21
|
+
"duration": "2s",
|
|
22
|
+
"assert": ["deaths == 1", "events.die == 1", "player.x < 200"]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "the lift carries the player over the pit",
|
|
26
|
+
"inputs": "right:0.3s, right+jump:0.45s, right:0.85s, right+jump:0.45s, right:0.35s, right+jump:0.45s, right:0.18s",
|
|
27
|
+
"duration": "4s",
|
|
28
|
+
"assert": ["coin-2.exists == false", "player.grounded == true", "player.x > 860", "deaths == 0"]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "the level can be completed without dying",
|
|
32
|
+
"inputs": "right:0.3s, right+jump:0.45s, right:0.85s, right+jump:0.45s, right:0.35s, right+jump:0.45s, right:0.18s, wait:1.0s, right+jump:0.45s, right:0.3s, wait:0.6s, right+jump:0.45s, right:0.1s, wait:0.6s, right:0.7s, right+jump:0.45s, right:0.2s, right+jump:0.45s, right:1.5s",
|
|
33
|
+
"assert": ["won == true", "deaths == 0", "score == 4", "events.stomp == 1", "slime.exists == false"]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|