Skip to content

Commit bffe807

Browse files
authored
Merge pull request #182 from WildCodeSchool/release/part-4
Release/part 4
2 parents 4b70bed + 5e26a70 commit bffe807

26 files changed

+699
-144
lines changed

packages/backend/api/src/game/creature/post.buy-creature.ts

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,6 @@ import { db } from '@app/backend-shared';
55

66
const postBuyCreature = Router();
77

8-
// check how many creatures we can unlock in this zone
9-
function getTotalCreaturesByZone(zoneId: number) {
10-
return db
11-
.selectFrom('creatures')
12-
.select([db.fn.count('creatures.id').as('quantityCreature')])
13-
.where('creatures.zone_id', '=', zoneId)
14-
.executeTakeFirst();
15-
}
16-
17-
//check how many creatures user has unlocked in this zone
18-
function getCreaturesUnlockedByZone(zoneId: number, parkId: number) {
19-
return db
20-
.selectFrom('park_creatures')
21-
.leftJoin('creatures', 'park_creatures.creature_id', 'creatures.id')
22-
.select([
23-
sql<number>`COUNT(DISTINCT ${sql.ref('park_creatures.creature_id')})`.as(
24-
'countCreaturesUnlockedByZone',
25-
),
26-
])
27-
.where('creatures.zone_id', '=', zoneId)
28-
.where('park_creatures.park_id', '=', parkId)
29-
.executeTakeFirst();
30-
}
31-
328
//Check if we have already the next zone in bdd
339
function getIsNextZoneUnlocked(parkId: number, zoneId: number) {
3410
return db
@@ -161,12 +137,7 @@ postBuyCreature.post('/buy', async (req: Request, res) => {
161137
})
162138
.executeTakeFirst();
163139

164-
const [isNextZoneUnlocked] = await Promise.all([
165-
getTotalCreaturesByZone(zoneId),
166-
getCreaturesUnlockedByZone(zoneId, parkId),
167-
getIsNextZoneUnlocked(parkId, zoneId),
168-
]);
169-
140+
const isNextZoneUnlocked = await getIsNextZoneUnlocked(parkId, zoneId);
170141
const zone = await db
171142
.selectFrom('zones')
172143
.select(['name'])
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/get.wallet.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ getWallet.get('/wallet', async (req: Request, res) => {
2929
return;
3030
}
3131

32+
if (park.wallet === null) {
33+
res.json({
34+
ok: false,
35+
message: 'no wallet for this user',
36+
});
37+
return;
38+
}
39+
3240
res.json({
3341
ok: true,
3442
park,

packages/backend/api/src/game/get.zones-count.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function getZones(parkId: number) {
1818
'zones.src_image',
1919
'zones.link',
2020
'park_zones.id as park_zone_id',
21+
'zones.required_qty',
2122
])
2223
.execute();
2324
}

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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { type Kysely, sql } from 'kysely';
2+
3+
import type { DB } from '@app/shared';
4+
5+
export async function up(db: Kysely<DB>): Promise<void> {
6+
// Migration code that update the database to the desired state.
7+
await db.transaction().execute(async (trx) => {
8+
await sql`
9+
ALTER TABLE zones
10+
ADD COLUMN required_qty INT NOT NULL;
11+
`.execute(trx);
12+
});
13+
}
14+
15+
export async function down(db: Kysely<DB>): Promise<void> {
16+
// Migration code that reverts the database to the previous state.
17+
await db.transaction().execute(async (trx) => {
18+
await sql`
19+
ALTER TABLE zones
20+
DROP COLUMN required_qty;
21+
`.execute(trx);
22+
});
23+
}

packages/frontend/web/src/components/barrier.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type BarrierProps = {
1919
};
2020

2121
export default function Barrier({ barrier, refetch }: BarrierProps) {
22-
const { wallet, walletRefetch } = useGameInfoContext();
22+
const { wallet, refetchWallet } = useGameInfoContext();
2323
const hasEnoughMoons = wallet >= barrier.price;
2424
const priceFormatted = formatNumber(barrier.price);
2525

@@ -41,7 +41,7 @@ export default function Barrier({ barrier, refetch }: BarrierProps) {
4141

4242
if (result.ok === true) {
4343
await refetch();
44-
await walletRefetch();
44+
await refetchWallet();
4545
}
4646
} catch (error) {
4747
// eslint-disable-next-line no-console

packages/frontend/web/src/components/bg-menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ type BgMenuProps = PropsWithChildren;
66
export default function BgMenu({ children }: BgMenuProps) {
77
return (
88
<div
9-
className='bg-primary-gray rounded p-3 shadow-[0px_4px_4px_rgba(0,0,0,0.25)] md:rounded-md md:p-6'
9+
className='bg-primary-gray rounded p-2 shadow-[0px_4px_4px_rgba(0,0,0,0.25)] md:rounded-md md:p-4'
1010
style={{ backgroundImage: `url(${BgPattern})` }}
1111
>
1212
{children}

0 commit comments

Comments
 (0)