Skip to content

Commit

Permalink
feat: allow item collection (#17)
Browse files Browse the repository at this point in the history
* feat: allow item collection

* feat: allow item collection with enter key

* fix: all items collected
  • Loading branch information
LukeSchlangen authored Mar 17, 2023
1 parent 4402645 commit e2a74b9
Show file tree
Hide file tree
Showing 16 changed files with 222 additions and 140 deletions.
16 changes: 9 additions & 7 deletions src/components/game-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import styles from 'src/styles/Mission.module.css'
// Redux
import { RootState } from '../redux/store'
import { useAppSelector, useAppDispatch } from '../redux/hooks'
import { moveUp, moveDown, moveLeft, moveRight } from '../redux/playerPositionSlice'
import { moveUp, moveDown, moveLeft, moveRight, collectItem } from '../redux/gameSlice'
import { useEffect } from 'react';

export default function Component() {
const playerPosition = useAppSelector((state: RootState) => state.playerPosition)
const { playerPosition } = useAppSelector((state: RootState) => state.game)
const dispatch = useAppDispatch()

function keyPressHandler({ key, keyCode }: any) {
function keyPressHandler({ key, keyCode }: { key: string | undefined, keyCode: number | undefined }) {
switch (key) {
case 'w':
return dispatch(moveUp())
Expand All @@ -23,14 +23,16 @@ export default function Component() {
}

switch (keyCode) {
case 38:
case 38: // up arrow
return dispatch(moveUp())
case 37:
case 37: // left arrow
return dispatch(moveLeft())
case 40:
case 40: // down arrow
return dispatch(moveDown())
case 39:
case 39: // right arrow
return dispatch(moveRight())
case 13: // enter
return dispatch(collectItem())
}
}

Expand Down
11 changes: 5 additions & 6 deletions src/components/inventory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ import { RootState } from 'src/redux/store';
import styles from 'src/styles/Mission.module.css'

export default function Component() {
const currentMission = useAppSelector((state: RootState) => state.mission)
const { inventory } = useAppSelector((state: RootState) => state.game)

return (
<section className={styles.inventory}>
<h2>Inventory</h2>
{JSON.stringify({currentMission})}
<div>
{currentMission.technologies.map(technology => (
{inventory.filter(item => item.status === 'COLLECTED').map(({title}) => (
<Image
key={technology}
src={`./google-cloud-icons/${technology}.svg`}
alt={`icon of ${technology}`}
key={title}
src={`./google-cloud-icons/${title}.svg`}
alt={`icon of ${title}`}
width='50'
height='50'
/>
Expand Down
6 changes: 5 additions & 1 deletion src/components/login-btn.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useSession, signIn, signOut } from "next-auth/react"
import { useEffect } from "react";
import { User } from "src/models/User";
import { startMission } from "src/redux/gameSlice";
import { useAppDispatch } from "src/redux/hooks";
import { setUser } from "src/redux/userSlice";

Expand All @@ -12,7 +13,10 @@ export default function Component() {
console.log('Fetching a user')
fetch('/api/user')
.then((response) => response.json())
.then((data: User) => dispatch(setUser(data)))
.then((user: User) => {
dispatch(setUser(user))
dispatch(startMission({ user }))
})
.catch(error => {
console.error('/api/user GET request did not work.', { error })
})
Expand Down
74 changes: 39 additions & 35 deletions src/components/mission-history.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,45 @@ export default function MissionHistory() {
<div className={styles.missionHistory}>
Completed Missions:
<table>
<tr>
<th>Title</th>
<th>Technologies</th>
<th>Learning Resources</th>
</tr>
{user.completedMissions.map(missionId => {
const mission = missions.find(mission => mission.id === missionId);
if (!mission) return null;
return (
<tr key={mission.id}>
<td>{mission.title}</td>
<td>
{mission.technologies.map(technology => (
<Image
key={technology}
src={`./google-cloud-icons/${technology}.svg`}
alt={`icon of ${technology}`}
width='50'
height='50'
/>
))}
</td>
<td>
{mission.learningResources.map(learningResource => (
<a
key={learningResource.link}
href={learningResource.link}
>
{learningResource.title}
</a>
))}
</td>
</tr>
)
})}
<thead>
<tr>
<th>Title</th>
<th>Technologies</th>
<th>Learning Resources</th>
</tr>
</thead>
<tbody>
{user.completedMissions.map((missionId, index) => {
const mission = missions.find(mission => mission.id === missionId);
if (!mission) return null;
return (
<tr key={`${mission.id}${index}`}>
<td>{mission.title}</td>
<td>
{mission.technologies.map(technology => (
<Image
key={technology}
src={`./google-cloud-icons/${technology}.svg`}
alt={`icon of ${technology}`}
width='50'
height='50'
/>
))}
</td>
<td>
{mission.learningResources.map(learningResource => (
<a
key={learningResource.link}
href={learningResource.link}
>
{learningResource.title}
</a>
))}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
)
Expand Down
4 changes: 2 additions & 2 deletions src/components/prompt-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import styles from 'src/styles/Mission.module.css'
import { LearningResource } from 'src/models/LearningResource';

export default function Component() {
const currentMission = useAppSelector((state: RootState) => state.mission)
const {mission: currentMission, status} = useAppSelector((state: RootState) => state.game)

return (
<section className={styles.prompts}>
Expand All @@ -18,7 +18,7 @@ export default function Component() {
<li>
Status:
{' '}
{currentMission.status}
{status}
</li>
</ul>
To learn more, check out these learning resources:
Expand Down
50 changes: 34 additions & 16 deletions src/components/tile.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,56 @@
import styles from 'src/styles/Mission.module.css'
import Image from 'next/image'
import { RootState } from '../redux/store'
import { useAppDispatch, useAppSelector } from '../redux/hooks'
import { GridPosition } from 'src/models/GridPosition';
import { nextMission } from 'src/redux/missionSlice';
import { setPlayerPosition } from 'src/redux/playerPositionSlice';
import { collectItem, startMission } from 'src/redux/gameSlice';
import { setUser } from 'src/redux/userSlice';
import { User } from 'src/models/User';

export default function Component({ x, y }: GridPosition) {
const dispatch = useAppDispatch()

const mission = useAppSelector((state: RootState) => state.mission)
const playerPosition = useAppSelector((state: RootState) => state.playerPosition)
const { playerPosition, mission, inventory } = useAppSelector((state: RootState) => state.game)
const playerIsOnTile = playerPosition.x === x && playerPosition.y === y;
const tileIsFinalTile = x == 2 && y == 2;

const tileItem = inventory.find(item => item.position.x === x && item.position.y === y && item.status === 'NOT_COLLECTED');
const allItemsCollected = inventory.length > 0 && inventory.every(item => item.status === 'COLLECTED');

const completeMission = () => {
dispatch(nextMission())
dispatch(setPlayerPosition({ x: 0, y: 0 }))
fetch('/api/user', {
method: 'POST',
body: JSON.stringify(mission),
}).then((response) => response.json())
.then((data: User) => dispatch(setUser(data)))
.catch(error => {
console.error('/api/user POST request did not work.', { error })
})
if (allItemsCollected) {
fetch('/api/user', {
method: 'POST',
body: JSON.stringify(mission),
}).then((response) => response.json())
.then((user: User) => {
dispatch(setUser(user))
dispatch(startMission({ user, nextMission: true }))
})
.catch(error => {
console.error('/api/user POST request did not work.', { error })
})
}
}

return (
<section className={styles.tile}>
{playerIsOnTile && 'X'}
{playerIsOnTile && tileIsFinalTile && (
<button onClick={(completeMission)}>
{tileItem && (
<Image
src={`./google-cloud-icons/${tileItem.title}.svg`}
alt={`icon of ${tileItem.title}`}
width='30'
height='30'
/>
)}
{playerIsOnTile && tileItem && (
<button onClick={() => dispatch(collectItem())}>
Collect Item {tileItem.title}
</button>
)}
{allItemsCollected && tileIsFinalTile && (
<button disabled={!playerIsOnTile} onClick={completeMission}>
Complete Mission
</button>
)}
Expand Down
2 changes: 0 additions & 2 deletions src/initialData.ts/missions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export const missions: Mission[] = [
"link": 'https://firebase.google.com/docs/hosting/nextjs'
},
],
status: 'NOT_STARTED',
},
{
"id": "Sb8XWrxLMykaBU7oZEMH",
Expand All @@ -23,6 +22,5 @@ export const missions: Mission[] = [
"link": 'https://cloud.google.com/nodejs/getting-started'
},
],
status: 'NOT_STARTED',
},
];
6 changes: 3 additions & 3 deletions src/lib/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class Database {
});
}

async setUser({email, completedMissions} :any): Promise<any> {
async setUser({email, completedMissions}: User): Promise<any> {
const userDoc = this.db.collection('users').doc(email);

return userDoc.set({
Expand All @@ -20,15 +20,15 @@ export class Database {
}, {merge: true});
}

async getUser({email}: any): Promise<User> {
async getUser({email}: {email: string}): Promise<User> {
const userDoc = this.db.collection('users').doc(email);
const snapshot = await userDoc.get();
const completedMissions = snapshot.data()?.completedMissions || [];

return { email, completedMissions }
}

async addCompletedMission({email, missionId} :any): Promise<any> {
async addCompletedMission({email, missionId}: {email: string, missionId: string}): Promise<any> {
const { completedMissions } = await this.getUser({email});
const updatedMissions = [ ...completedMissions, missionId ]

Expand Down
6 changes: 3 additions & 3 deletions src/models/GridPosition.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export class GridPosition {
x!: number;
y!: number;
}
x!: number;
y!: number;
}

8 changes: 8 additions & 0 deletions src/models/InventoryItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { GridPosition } from "./GridPosition";

export class InventoryItem {
position!: GridPosition;
status!: string;
title!: string;
}

3 changes: 0 additions & 3 deletions src/models/Mission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,4 @@ export class Mission {
title!: string;
technologies!: string[];
learningResources!: LearningResource[];

// status is not stored in the database, it is calculated from the user object
status!: string;
}
Loading

0 comments on commit e2a74b9

Please sign in to comment.