Skip to content

Commit be7cee0

Browse files
committed
feat(tiles): add an optional 3D effect to tiles
1 parent 2ee478e commit be7cee0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+130
-32
lines changed

src/app/components/board/board.component.html

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,23 @@
9999
stroke: black;
100100
}
101101

102+
g.stage.tile3d g.draw rect.shadow {
103+
fill: #a89488;
104+
opacity: 1;
105+
stroke-width: 0;
106+
stroke: none;
107+
}
108+
109+
g.stage.tile3d g.draw rect.stone {
110+
stroke: #a09085;
111+
}
112+
113+
g.stage.tile3d g.draw rect.bevel {
114+
fill: none;
115+
stroke-width: 1.5;
116+
stroke: rgba(255, 255, 255, 0.6);
117+
}
118+
102119
g.hidden {
103120
opacity: 0;
104121
}
@@ -111,6 +128,11 @@
111128
opacity: 0.7;
112129
}
113130

131+
g.stage.tile3d.contrast g.draw rect.shadow {
132+
fill: #7a6c63;
133+
opacity: 1;
134+
}
135+
114136
g.stage.dark g.draw rect.shadow {
115137
opacity: 0.6;
116138
stroke-width: 1;
@@ -124,6 +146,24 @@
124146
stroke: #9d948c;
125147
}
126148

149+
g.stage.dark.tile3d g.draw rect.shadow {
150+
opacity: 1;
151+
stroke-width: 0;
152+
fill: #5a524e;
153+
stroke: none;
154+
}
155+
156+
g.stage.dark.tile3d g.draw rect.stone {
157+
fill: #1a1512;
158+
stroke: #6a5e58;
159+
}
160+
161+
g.stage.dark.tile3d g.draw rect.bevel {
162+
fill: none;
163+
stroke-width: 1.5;
164+
stroke: rgba(255, 255, 255, 0.12);
165+
}
166+
127167
g.stage.dark g.selected g.tile rect.stone {
128168
stroke: #F8EABB;
129169
fill: black;
@@ -145,6 +185,11 @@
145185
opacity: 0.7;
146186
}
147187

188+
g.stage.dark.tile3d.contrast g.draw rect.shadow {
189+
fill: #3e3834;
190+
opacity: 1;
191+
}
192+
148193
g.stage.dark .front {
149194
display: none;
150195
}
@@ -160,7 +205,7 @@
160205
}
161206
</style>
162207
<defs app-image-set-loader [imageSet]="imageSet()" [kyodaiUrl]="kyodaiUrl()" [dark]="app.settings.dark" [prefix]="prefix"></defs>
163-
<g class="stage" [attr.transform]="transformStage" [class.dark]="app.settings.dark" [class.contrast]="app.settings.contrast">
208+
<g class="stage" [attr.transform]="transformStage" [class.dark]="app.settings.dark" [class.contrast]="app.settings.contrast" [class.tile3d]="app.settings.tile3d">
164209
@for (draw of drawStones; track draw.source.z + ':' + draw.source.x + ':' + draw.source.y) {
165210
<g class="draw"
166211
[class.selected]="draw.source.selected"
@@ -174,8 +219,14 @@
174219
@if (draw.url) {
175220
<title>{{ draw.url | translate }}</title>
176221
}
177-
<rect class="shadow" x="3" y="3" width="75" height="100" rx="10" ry="10"></rect>
178-
<rect class="stone" x="0" y="0" width="75" height="100" rx="10" ry="10"></rect>
222+
@if (app.settings.tile3d) {
223+
<rect class="shadow" x="5" y="5" width="75" height="100" rx="10" ry="10"></rect>
224+
<rect class="stone" x="0" y="0" width="75" height="100" rx="10" ry="10"></rect>
225+
<rect class="bevel" x="1" y="1" width="73" height="98" rx="9" ry="9"></rect>
226+
} @else {
227+
<rect class="shadow" x="3" y="3" width="75" height="100" rx="10" ry="10"></rect>
228+
<rect class="stone" x="0" y="0" width="75" height="100" rx="10" ry="10"></rect>
229+
}
179230
<use
180231
[attr.xlink:href]="draw.url | prefix:urlPrefix"
181232
[attr.x]="imagePos[0]"

src/app/components/settings/settings.component.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@
102102
<fieldset>
103103
<legend id="tiles-legend">{{ 'TILES' | translate }}</legend>
104104
<ul class="checks" role="group" aria-labelledby="tiles-legend">
105+
<li>
106+
<label class="check">
107+
<input type="checkbox"
108+
[checked]="app.settings.tile3d"
109+
(change)="app.settings.tile3d = !app.settings.tile3d;app.settings.save();"
110+
aria-label="{{ 'TILES_3D' | translate }}"
111+
>
112+
<span>{{ 'TILES_3D' | translate }}</span>
113+
</label>
114+
</li>
105115
<li>
106116
<label class="check">
107117
<input type="checkbox"

src/app/components/settings/settings.component.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ describe('SettingsComponent', () => {
164164
expect(radioButtons).toHaveLength(expectedCount);
165165
});
166166

167-
it('should render contrast and dark mode checkboxes', () => {
167+
it('should render contrast, dark mode and 3D checkboxes', () => {
168168
const checkboxes = fixture.debugElement.queryAll(By.css('input[type="checkbox"]'));
169-
expect(checkboxes).toHaveLength(2);
169+
expect(checkboxes).toHaveLength(3);
170170
});
171171

172172
it('should render clear best times button', () => {

src/app/model/draw.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe('Draw', () => {
1717
expect(pos.translate).toBe(`translate(${pos.x},${pos.y})`);
1818

1919
// Check z calculation specifically
20-
expect(pos.sort).toBe(3 + CONSTS.mY * (2 + CONSTS.mX));
20+
expect(pos.sort).toBe(3 + 2 + CONSTS.mY * (2 + CONSTS.mX));
2121
});
2222
});
2323

src/app/model/draw.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function calcDrawPos(z: number, x: number, y: number): DrawPos {
2929
y: ((CONSTS.tileHeight + 2) * y - (z * CONSTS.levelOffset)) / 2,
3030
w: (CONSTS.tileWidth + 2) + (z * CONSTS.levelOffset),
3131
h: (CONSTS.tileHeight + 2) + (z * CONSTS.levelOffset),
32-
sort: y + CONSTS.mY * (x + CONSTS.mX) * z,
32+
sort: y + x + CONSTS.mY * (x + CONSTS.mX) * z,
3333
translate: ''
3434
};
3535
pos.translate = `translate(${pos.x},${pos.y})`;

src/app/model/indicator.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ export class Indicator {
1919
return;
2020
}
2121

22-
// Find the full indicator object in the array
23-
const fullIndicator = this.gestureIndicators.find(
24-
gi => gi.x === indicator.x && gi.y === indicator.y
25-
);
22+
// Find by reference first, then fall back to coordinate match
23+
const fullIndicator =
24+
this.gestureIndicators.find(gi => gi === indicator) ??
25+
this.gestureIndicators.find(gi => gi.x === indicator.x && gi.y === indicator.y);
2626

2727
if (!fullIndicator) {
2828
return;

src/app/model/settings.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe('Settings', () => {
3333
expect(settings.music).toBe(false);
3434
expect(settings.contrast).toBe(false);
3535
expect(settings.dark).toBe(false);
36+
expect(settings.tile3d).toBe(false);
3637
expect(settings.background).toBe('');
3738
expect(settings.theme).toBe(ThemeDefault);
3839
expect(settings.stats).toEqual({
@@ -50,6 +51,7 @@ describe('Settings', () => {
5051
music: true,
5152
contrast: true,
5253
dark: true,
54+
tile3d: true,
5355
background: 'test-background',
5456
theme: 'test-theme',
5557
tileset: 'test-tileset',
@@ -66,6 +68,7 @@ describe('Settings', () => {
6668
expect(settings.music).toBe(true);
6769
expect(settings.contrast).toBe(true);
6870
expect(settings.dark).toBe(true);
71+
expect(settings.tile3d).toBe(true);
6972
expect(settings.background).toBe('test-background');
7073
expect(settings.theme).toBe('test-theme');
7174
expect(settings.tileset).toBe('test-tileset');
@@ -107,6 +110,7 @@ describe('Settings', () => {
107110
settings.music = true;
108111
settings.contrast = true;
109112
settings.dark = true;
113+
settings.tile3d = true;
110114
settings.background = 'test-background';
111115
settings.theme = 'test-theme';
112116
settings.tileset = 'test-tileset';
@@ -121,6 +125,7 @@ describe('Settings', () => {
121125
music: true,
122126
contrast: true,
123127
dark: true,
128+
tile3d: true,
124129
background: 'test-background',
125130
pattern: undefined,
126131
theme: 'test-theme',

src/app/model/settings.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export class Settings {
88
music = false;
99
contrast = false;
1010
dark = false;
11+
tile3d = false;
1112
background = '';
1213
pattern?: string;
1314
kyodaiUrl?: string;
@@ -32,6 +33,7 @@ export class Settings {
3233
this.theme = store.theme ?? ThemeDefault;
3334
this.contrast = store.contrast ?? false;
3435
this.dark = store.dark ?? false;
36+
this.tile3d = store.tile3d ?? false;
3537
this.sounds = store.sounds ?? this.sounds;
3638
this.music = store.music ?? this.music;
3739
this.kyodaiUrl = store.kyodaiUrl;
@@ -52,6 +54,7 @@ export class Settings {
5254
music: this.music,
5355
contrast: this.contrast,
5456
dark: this.dark,
57+
tile3d: this.tile3d,
5558
background: this.background,
5659
pattern: this.pattern,
5760
theme: this.theme,

src/app/model/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,6 @@ export class SettingsStore {
8989
theme: string;
9090
background: string;
9191
pattern?: string;
92+
tile3d?: boolean;
9293
tutorialCompleted?: boolean;
9394
}

src/assets/i18n/ar.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
"TILES_JOKERS_RANK": "الرموز البدلاء المقيدة بالرتبة",
140140
"TILES_EXTRA": "إضافي",
141141
"TILES_DARK": "مظلم",
142+
"TILES_3D": "ثلاثي الأبعاد",
142143
"THEME": "المظهر",
143144
"THEME_SAGE": "أخضر المريمية",
144145
"THEME_FOREST": "أخضر الغابة",
@@ -283,4 +284,3 @@
283284
"TUTORIAL_WELCOME_BENEFIT_3": "تخطي في أي وقت والقفز إلى لعبة",
284285
"TUTORIAL_WELCOME_START": "ابدأ البرنامج التعليمي"
285286
}
286-

0 commit comments

Comments
 (0)