Skip to content

Commit 989ae7e

Browse files
authored
Merge pull request #180 from WildCodeSchool/feature/daily-gift
Feature/daily gift
2 parents 88642ab + eeb0a63 commit 989ae7e

File tree

8 files changed

+487
-1
lines changed

8 files changed

+487
-1
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import { type Request, Router } from 'express';
2+
import { sql } from 'kysely';
3+
4+
import { db } from '@app/backend-shared';
5+
import { generateName } from '@app/shared';
6+
7+
const getGiftRoute = Router();
8+
9+
//check if last received gift more than 24h
10+
function getLastGift(parkId: number) {
11+
return db
12+
.selectFrom('park_gifts')
13+
.select(['gift_date'])
14+
.where('park_id', '=', parkId)
15+
.where('gift_date', '>', sql<Date>`NOW() - INTERVAL 24 HOUR`)
16+
.execute();
17+
}
18+
19+
//function insert into parks_gift
20+
async function insertGift(parkId: number, type: string, value: string) {
21+
await db
22+
.insertInto('park_gifts')
23+
.values({
24+
type,
25+
gift_date: sql`NOW()`,
26+
value,
27+
park_id: parkId,
28+
})
29+
.executeTakeFirst();
30+
}
31+
32+
//function updateWllet
33+
async function updateWallet(parkId: number, amount: number) {
34+
await db
35+
.updateTable('parks')
36+
.set((eb) => ({
37+
wallet: eb('wallet', '+', amount),
38+
}))
39+
.where('id', '=', parkId)
40+
.executeTakeFirst();
41+
}
42+
43+
type Creature = {
44+
id: number;
45+
feed_timer: number | null;
46+
src_image: string | null;
47+
species: string;
48+
};
49+
50+
//function insert creature into park_creatures
51+
async function insertCreatureToPark(parkId: number, creature: Creature) {
52+
const name = generateName();
53+
const gender = Math.random() < 0.5 ? 'male' : 'female';
54+
55+
await db
56+
.insertInto('park_creatures')
57+
.values({
58+
name,
59+
gender,
60+
is_adult: 1,
61+
is_parent: 0,
62+
feed_date: sql`NOW() + INTERVAL ${creature.feed_timer} MINUTE`,
63+
adult_at: sql`NOW()`,
64+
park_id: parkId,
65+
creature_id: creature.id,
66+
})
67+
.executeTakeFirst();
68+
}
69+
70+
type Visitor = {
71+
id: number;
72+
category: string;
73+
src_image: string | null;
74+
};
75+
76+
//function insert visitor into park_visitors
77+
async function insertVisitorToPark(parkId: number, visitor: Visitor) {
78+
await db
79+
.insertInto('park_visitors')
80+
.values({
81+
entry_time: sql`NOW()`,
82+
exit_time: sql`NOW() + INTERVAL 4 HOUR`,
83+
park_id: parkId,
84+
visitor_id: visitor.id,
85+
})
86+
.executeTakeFirst();
87+
}
88+
89+
getGiftRoute.get('/gift', async (req: Request, res) => {
90+
const parkId = req.parkId;
91+
if (parkId === undefined) {
92+
res.json({
93+
ok: false,
94+
});
95+
return;
96+
}
97+
98+
const lastGift = await getLastGift(parkId);
99+
100+
//if last gift less than 24H
101+
if (lastGift.length > 0) {
102+
res.json({
103+
ok: false,
104+
message: 'Too soon for a new gift.',
105+
});
106+
return;
107+
}
108+
109+
const gifts = ['creature', 'visitor', 'moons'];
110+
const giftType = gifts[Math.floor(Math.random() * gifts.length)];
111+
112+
//if creature is drawn
113+
if (giftType === 'creature') {
114+
//check unlocked creatures
115+
const creatures = await db
116+
.selectFrom('park_creatures')
117+
.innerJoin('creatures', 'creatures.id', 'park_creatures.creature_id')
118+
.select([
119+
'creatures.id',
120+
'creatures.feed_timer',
121+
'creatures.src_image',
122+
'creatures.species',
123+
])
124+
.where('park_id', '=', parkId)
125+
.distinct()
126+
.execute();
127+
128+
//if no creature unlocked yet insert a default creature
129+
if (creatures.length === 0) {
130+
const defaultCreature = await db
131+
.selectFrom('creatures')
132+
.select(['id', 'species', 'feed_timer', 'src_image'])
133+
.where('id', '=', 1)
134+
.executeTakeFirst();
135+
136+
if (defaultCreature === undefined) {
137+
res.json({
138+
ok: false,
139+
message: 'no creature available',
140+
});
141+
return;
142+
}
143+
144+
await insertGift(parkId, 'creature', defaultCreature.species);
145+
await insertCreatureToPark(parkId, defaultCreature);
146+
}
147+
148+
//else draw a creature from the unlocked ones
149+
const chosen = creatures[Math.floor(Math.random() * creatures.length)];
150+
151+
await insertGift(parkId, 'creature', chosen.species);
152+
await insertCreatureToPark(parkId, chosen);
153+
154+
res.json({
155+
ok: true,
156+
gift: {
157+
type: 'creature',
158+
creatureId: chosen.id,
159+
image: chosen.src_image,
160+
},
161+
});
162+
return;
163+
}
164+
165+
//if visitor is drawn
166+
if (giftType === 'visitor') {
167+
//check unlocked visitors
168+
const visitors = await db
169+
.selectFrom('park_visitors')
170+
.innerJoin('visitors', 'visitors.zone_id', 'park_visitors.visitor_id')
171+
.select([
172+
'visitors.id',
173+
'visitors.category',
174+
'visitors.src_image',
175+
'visitors.entry_price',
176+
])
177+
.where('park_id', '=', parkId)
178+
.distinct()
179+
.execute();
180+
181+
//if no visitors unlocked yet insert a default visitor
182+
if (visitors.length === 0) {
183+
const defaultVisitor = await db
184+
.selectFrom('visitors')
185+
.select(['id', 'category', 'src_image', 'entry_price'])
186+
.where('id', '=', 1)
187+
.executeTakeFirst();
188+
189+
if (defaultVisitor === undefined) {
190+
res.json({
191+
ok: false,
192+
message: 'no creature available',
193+
});
194+
return;
195+
}
196+
197+
await insertGift(parkId, 'visitor', `visitor-${defaultVisitor.category}`);
198+
await insertVisitorToPark(parkId, defaultVisitor);
199+
await updateWallet(parkId, defaultVisitor.entry_price);
200+
}
201+
202+
//else draw a visitor from the unlocked ones
203+
const chosen = visitors[Math.floor(Math.random() * visitors.length)];
204+
205+
await insertGift(parkId, 'visitor', `visitor- ${chosen.category}`);
206+
await insertVisitorToPark(parkId, chosen);
207+
await updateWallet(parkId, chosen.entry_price);
208+
209+
res.json({
210+
ok: true,
211+
gift: {
212+
type: 'visitor',
213+
creatureId: chosen.category,
214+
image: chosen.src_image,
215+
},
216+
});
217+
return;
218+
}
219+
220+
//if moons is drawn
221+
if (giftType === 'moons') {
222+
const amounts = [500, 1000, 1500, 2000];
223+
const prize = amounts[Math.floor(Math.random() * amounts.length)];
224+
225+
await insertGift(parkId, 'moons', String(prize));
226+
await updateWallet(parkId, prize);
227+
228+
res.json({
229+
ok: true,
230+
gift: {
231+
type: 'moons',
232+
moons: prize,
233+
},
234+
});
235+
return;
236+
}
237+
238+
res.json({ ok: false });
239+
});
240+
241+
export default getGiftRoute;

packages/backend/api/src/game/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import getAvatars from './get.avatars';
88
import getCreaturesMenuRoute from './get.creatures-menu';
99
import getDecorations from './get.decorations';
1010
import getEnclosures from './get.enclosures';
11+
import getGiftRoute from './get.gift';
1112
import getLeaderboardRoute from './get.leaderboard';
1213
import getParkUser from './get.park-user';
1314
import getWallet from './get.wallet';
@@ -30,5 +31,6 @@ gameRouter.use('/visitors', visitorRouter);
3031
gameRouter.use(getLeaderboardRoute);
3132
gameRouter.use(getAvatars);
3233
gameRouter.use(getCreaturesMenuRoute);
34+
gameRouter.use(getGiftRoute);
3335

3436
export default gameRouter;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { PropsWithChildren } from 'react';
2+
3+
import Chest from '../assets/images/deco/chest.png';
4+
import Moons from '../assets/images/icons-buttons/moon.png';
5+
6+
type DailyGiftProps = PropsWithChildren<{
7+
readonly src_image?: string;
8+
readonly type: 'creature' | 'visitor' | 'moons';
9+
readonly amount?: number;
10+
readonly onClick: () => void;
11+
}>;
12+
13+
export default function DailyGift({
14+
src_image,
15+
type,
16+
amount,
17+
onClick,
18+
}: DailyGiftProps) {
19+
return (
20+
<div
21+
onClick={onClick}
22+
className='fixed inset-0 flex justify-center bg-black/70'
23+
>
24+
<div className='animate-lights-on h-screen w-70 bg-gradient-to-b from-white/50 to-white/0 [clip-path:polygon(45%_0%,55%_0%,100%_100%,0%_100%)]' />
25+
<img
26+
src={Chest}
27+
alt='chest'
28+
className='animate-scale absolute mt-25 w-25 drop-shadow-[0_5px_10px_white] md:mt-35 md:w-40'
29+
/>
30+
31+
{type === 'moons' ? (
32+
<div className='absolute mt-85 flex flex-col items-center gap-3 text-white md:mt-120'>
33+
<p className='text-3xl font-bold drop-shadow-[0_2px_4px_black] md:text-4xl'>
34+
{amount}
35+
</p>
36+
<img src={Moons} alt='moons' className='w-10 md:w-16' />
37+
</div>
38+
) : (
39+
<img
40+
src={`/images/creatures/${src_image}`}
41+
alt='gift'
42+
className='animate-drop absolute mt-85 w-18 drop-shadow-[0_5px_10px_white] md:mt-120 md:w-25'
43+
style={{
44+
animationDelay: '2s',
45+
animationFillMode: 'forwards',
46+
opacity: 0,
47+
}}
48+
/>
49+
)}
50+
</div>
51+
);
52+
}

packages/frontend/web/src/globals.css

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ body {
7070

7171
/* wallet */
7272
--animate-wallet-up: walletUp 2s ease-in-out forwards;
73+
74+
/* gifts light */
75+
--animate-lights-on: lightsOn 3s ease-in-out;
76+
77+
/* chest apparition */
78+
--animate-scale: chestApparition 2s ease-in-out;
79+
80+
/* drop gift */
81+
--animate-drop: dropGift 1s ease-out forwards;
7382
}
7483

7584
@keyframes typing1 {
@@ -201,6 +210,64 @@ body {
201210
}
202211
/* End animation wallet */
203212

213+
/* Daily gift animations */
214+
/* lights on */
215+
@keyframes lightsOn {
216+
0% {
217+
opacity: 0;
218+
}
219+
5% {
220+
opacity: 0;
221+
}
222+
7% {
223+
opacity: 1;
224+
}
225+
10% {
226+
opacity: 0;
227+
}
228+
15% {
229+
opacity: 0;
230+
}
231+
18% {
232+
opacity: 1;
233+
}
234+
20% {
235+
opacity: 0;
236+
}
237+
33% {
238+
opacity: 0;
239+
}
240+
35% {
241+
opacity: 1;
242+
}
243+
}
244+
245+
/* Chest apparition */
246+
@keyframes chestApparition {
247+
from {
248+
transform: scale(0);
249+
opacity: 0;
250+
}
251+
to {
252+
transform: scale(1);
253+
opacity: 1;
254+
}
255+
}
256+
257+
/* Drop gift */
258+
@keyframes dropGift {
259+
0% {
260+
transform: translateY(-200px);
261+
opacity: 0;
262+
}
263+
100% {
264+
transform: translateY(0);
265+
opacity: 1;
266+
}
267+
}
268+
269+
/* End of Daily gift animations */
270+
204271
.text-outline-purple {
205272
-webkit-text-stroke-width: 4px;
206273
-webkit-text-stroke-color: #4d40a8;

0 commit comments

Comments
 (0)