Compare commits
3 Commits
2202bd606f
...
243c50a1a0
Author | SHA1 | Date | |
---|---|---|---|
243c50a1a0 | |||
a624ba1e73 | |||
dd32327383 |
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + React + TS</title>
|
<title>Idle Pet</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
@ -48,7 +48,10 @@ export default function App() {
|
|||||||
<AnimatedBackground />
|
<AnimatedBackground />
|
||||||
<div className="relative z-10 min-h-screen backdrop-blur-sm bg-gray-900/20 text-white p-4">
|
<div className="relative z-10 min-h-screen backdrop-blur-sm bg-gray-900/20 text-white p-4">
|
||||||
<div className="max-w-4xl mx-auto grid gap-8">
|
<div className="max-w-4xl mx-auto grid gap-8">
|
||||||
<PetDisplay pet={pet} />
|
<PetDisplay
|
||||||
|
pet={pet}
|
||||||
|
onPetUpdate={handlePetUpdate} // Add this prop
|
||||||
|
/>
|
||||||
<InteractionMenu
|
<InteractionMenu
|
||||||
pet={pet}
|
pet={pet}
|
||||||
onPetUpdate={handlePetUpdate}
|
onPetUpdate={handlePetUpdate}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import { Pet } from '../types/Pet';
|
import { Pet } from '../types/Pet';
|
||||||
import { Brain, Dumbbell, Heart, Sparkles, Coins, Pizza, Trash2, Trophy } from 'lucide-react';
|
import { Brain, Dumbbell, Heart, Sparkles, Coins, Pizza, Trash2, Trophy } from 'lucide-react';
|
||||||
import { PET_CLASSES } from '../data/petClasses';
|
import { PET_CLASSES } from '../data/petClasses';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import InventoryModal from './modal/InventoryModal';
|
||||||
|
import SkillTreeModal from './modal/SkillTreeModal';
|
||||||
|
|
||||||
interface PetDisplayProps {
|
interface PetDisplayProps {
|
||||||
pet: Pet;
|
pet: Pet;
|
||||||
|
onPetUpdate: (updatedPet: Pet) => void; // Add this prop
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PetDisplay({ pet }: PetDisplayProps) {
|
export default function PetDisplay({ pet, onPetUpdate }: PetDisplayProps) {
|
||||||
const StatBar = ({ value, maxValue, label, icon: Icon }: { value: number; maxValue: number; label: string; icon: any }) => (
|
const StatBar = ({ value, maxValue, label, icon: Icon }: { value: number; maxValue: number; label: string; icon: any }) => (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Icon className="w-5 h-5" />
|
<Icon className="w-5 h-5" />
|
||||||
@ -28,6 +32,9 @@ export default function PetDisplay({ pet }: PetDisplayProps) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [showInventoryModal, setShowInventoryModal] = useState(false);
|
||||||
|
const [showSkillTreeModal, setShowSkillTreeModal] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-900 p-6 rounded-xl shadow-xl">
|
<div className="bg-gray-900 p-6 rounded-xl shadow-xl">
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
@ -67,12 +74,42 @@ export default function PetDisplay({ pet }: PetDisplayProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="flex items-start mb-4 space-x-4">
|
||||||
<ResourceCounter value={pet.resources.wisdom} label="Wisdom" icon={Sparkles} />
|
<div className="grid grid-cols-2 gap-4 flex-1">
|
||||||
<ResourceCounter value={pet.resources.gold} label="Gold" icon={Coins} />
|
<ResourceCounter value={pet.resources.wisdom} label="Wisdom" icon={Sparkles} />
|
||||||
<ResourceCounter value={pet.resources.food} label="Food" icon={Pizza} />
|
<ResourceCounter value={pet.resources.gold} label="Gold" icon={Coins} />
|
||||||
<ResourceCounter value={pet.resources.junk} label="Junk" icon={Trash2} />
|
<ResourceCounter value={pet.resources.food} label="Food" icon={Pizza} />
|
||||||
|
<ResourceCounter value={pet.resources.junk} label="Junk" icon={Trash2} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowInventoryModal(true)}
|
||||||
|
className="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded"
|
||||||
|
>
|
||||||
|
Inventory
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowSkillTreeModal(true)}
|
||||||
|
className="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded"
|
||||||
|
>
|
||||||
|
Skill Tree
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{showInventoryModal && (
|
||||||
|
<InventoryModal
|
||||||
|
inventory={pet.inventory}
|
||||||
|
petId={pet.id}
|
||||||
|
onClose={() => setShowInventoryModal(false)}
|
||||||
|
onPetUpdate={onPetUpdate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showSkillTreeModal && (
|
||||||
|
<SkillTreeModal
|
||||||
|
onClose={() => setShowSkillTreeModal(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
95
src/components/modal/InventoryModal.tsx
Normal file
95
src/components/modal/InventoryModal.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Inventory, InvItemInteraction, Pet } from '../../types/Pet';
|
||||||
|
import { putPetItemInteract } from '../../services/api/api';
|
||||||
|
|
||||||
|
interface InventoryModalProps {
|
||||||
|
inventory: Inventory;
|
||||||
|
petId: string;
|
||||||
|
onClose: () => void;
|
||||||
|
onPetUpdate: (updatedPet: Pet) => void; // Add this prop
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function InventoryModal({ inventory, petId, onClose, onPetUpdate }: InventoryModalProps) {
|
||||||
|
const capacity = inventory.capacity; // Expected to be 20 for 4 rows x 5 cols
|
||||||
|
const items = inventory.items;
|
||||||
|
const gridSlots = Array.from({ length: capacity }, (_, i) => items[i]);
|
||||||
|
const [selectedItemIndex, setSelectedItemIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const handleItemClick = (index: number) => {
|
||||||
|
if (selectedItemIndex === index || gridSlots[index] === undefined) {
|
||||||
|
setSelectedItemIndex(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setSelectedItemIndex(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInteraction = async (interaction: InvItemInteraction) => {
|
||||||
|
if (selectedItemIndex === null || gridSlots[selectedItemIndex] === undefined) return;
|
||||||
|
try {
|
||||||
|
const updatedPet = await putPetItemInteract(petId, gridSlots[selectedItemIndex], interaction);
|
||||||
|
onPetUpdate(updatedPet);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Item interaction failed:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 z-50">
|
||||||
|
<div className="bg-gray-800 p-6 rounded-xl max-w-2xl w-full">
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-2xl font-bold">Inventory</h2>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="bg-red-600 hover:bg-red-700 text-white px-2 py-1 rounded"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-5 grid-rows-4 gap-4">
|
||||||
|
{gridSlots.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
onClick={() => handleItemClick(index)}
|
||||||
|
className={`border rounded p-4 flex items-center justify-center h-16
|
||||||
|
${item !== undefined ? 'bg-gray-700 cursor-pointer' : 'bg-gray-800 text-gray-500'}
|
||||||
|
${selectedItemIndex === index ? 'border-blue-500 animate-pulse' : 'border-gray-600'}`}
|
||||||
|
>
|
||||||
|
{item !== undefined ? <span>{item}</span> : <span className="text-gray-500">Empty</span>}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 grid grid-cols-4 gap-4">
|
||||||
|
<button
|
||||||
|
disabled={selectedItemIndex === null}
|
||||||
|
onClick={() => handleInteraction('USE')}
|
||||||
|
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Use
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={selectedItemIndex === null}
|
||||||
|
onClick={() => handleInteraction('DROP')}
|
||||||
|
className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Drop
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={selectedItemIndex === null}
|
||||||
|
onClick={() => handleInteraction('EQUIP')}
|
||||||
|
className="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Equip
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={selectedItemIndex === null}
|
||||||
|
onClick={() => handleInteraction('UNEQUIP')}
|
||||||
|
className="bg-yellow-600 hover:bg-yellow-700 text-white px-4 py-2 rounded disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Unequip
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
26
src/components/modal/SkillTreeModal.tsx
Normal file
26
src/components/modal/SkillTreeModal.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface SkillTreeModalProps {
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SkillTreeModal({ onClose }: SkillTreeModalProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 z-50"
|
||||||
|
>
|
||||||
|
<div className="bg-gray-800 p-6 rounded-xl max-w-2xl w-full">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">Skill Tree</h2>
|
||||||
|
<p className="text-gray-400 mb-6">Skill tree modal under development.</p>
|
||||||
|
<div className="mt-6 flex justify-end">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,57 +1,53 @@
|
|||||||
import { ApiService } from './index';
|
import { ApiService } from './index';
|
||||||
import { Pet, Resources } from '../../types/Pet';
|
import { InvItemInteraction, Pet, Resources } from '../../types/Pet';
|
||||||
import { PetCreationRequest } from '../../types/PetCreationRequest';
|
import { PetCreationRequest } from '../../types/PetCreationRequest';
|
||||||
import { PetUpdateActionRequest } from '../../types/PetUpdateActionRequest';
|
import { PetUpdateActionRequest } from '../../types/PetUpdateActionRequest';
|
||||||
|
import { PetSkill, Skill } from '../../types/Skills';
|
||||||
|
|
||||||
// Get API service instance
|
// Get API service instance
|
||||||
const api = ApiService.getInstance();
|
const api = ApiService.getInstance();
|
||||||
|
|
||||||
export async function fetchPets(): Promise<Pet[]> {
|
export async function fetchPets(): Promise<Pet[]> {
|
||||||
try {
|
const response = await api.get<Pet[]>('/api/v1/pet');
|
||||||
const response = await api.get<Pet[]>('/api/v1/pet');
|
return response.data;
|
||||||
return response.data;
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Failed to fetch pets:', error.message);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createPet(data: PetCreationRequest): Promise<Pet> {
|
export async function createPet(data: PetCreationRequest): Promise<Pet> {
|
||||||
try {
|
const response = await api.post<Pet>('/api/v1/pet', data);
|
||||||
const response = await api.post<Pet>('/api/v1/pet', data);
|
return response.data;
|
||||||
return response.data;
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Failed to create pet:', error.message);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updatePetAction(petId: string, data: PetUpdateActionRequest): Promise<Pet> {
|
export async function updatePetAction(petId: string, data: PetUpdateActionRequest): Promise<Pet> {
|
||||||
try {
|
const response = await api.put<Pet>(`/api/v1/pet/${petId}/action`, data);
|
||||||
const response = await api.put<Pet>(`/api/v1/pet/${petId}/action`, data);
|
return response.data;
|
||||||
return response.data;
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Failed to update pet action:', error.message);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPetGatheredResources(petId: string): Promise<Resources> {
|
export async function getPetGatheredResources(petId: string): Promise<Resources> {
|
||||||
try {
|
const response = await api.get<Resources>(`/api/v1/pet/${petId}/resources/gathered`);
|
||||||
const response = await api.get<Resources>(`/api/v1/pet/${petId}/resources/gathered`);
|
return response.data;
|
||||||
return response.data;
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Failed to fetch pet gathered resources:', error.message);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function putPetCollectResources(petId: string): Promise<Pet> {
|
export async function putPetCollectResources(petId: string): Promise<Pet> {
|
||||||
try {
|
const response = await api.put<Pet>(`/api/v1/pet/${petId}/resources/collect`);
|
||||||
const response = await api.put<Pet>(`/api/v1/pet/${petId}/resources/collect`);
|
return response.data;
|
||||||
return response.data;
|
}
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Failed to collect pet gathered resources:', error.message);
|
export async function getAllSkills(): Promise<Skill[]> {
|
||||||
throw error;
|
const response = await api.get<Skill[]>(`/api/v1/skill`);
|
||||||
}
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPetSkills(petId: string): Promise<PetSkill[]> {
|
||||||
|
const response = await api.get<PetSkill[]>(`/api/v1/skill/${petId}/pet`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function postAllocatePetSkill(petId: string, skillId: number): Promise<PetSkill> {
|
||||||
|
const response = await api.post<PetSkill>(`/api/v1/skill/${petId}/allocate/${skillId}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function putPetItemInteract(petId: string, itemId: number, inter: InvItemInteraction): Promise<Pet> {
|
||||||
|
const response = await api.put<Pet>(`/api/v1/inventory/${petId}/${itemId}/${inter.toLowerCase()}`);
|
||||||
|
return response.data;
|
||||||
}
|
}
|
@ -25,9 +25,20 @@ export interface Pet {
|
|||||||
stats: PetStats;
|
stats: PetStats;
|
||||||
resources: Resources;
|
resources: Resources;
|
||||||
level: number;
|
level: number;
|
||||||
petBasicAction: PetBasicAction;
|
experience: number;
|
||||||
basicActionCooldown: Date;
|
health: number;
|
||||||
|
maxHealth: number;
|
||||||
petGatherAction: PetGatherAction;
|
petGatherAction: PetGatherAction;
|
||||||
|
basicActionCooldown: Date;
|
||||||
|
petBasicAction: PetBasicAction;
|
||||||
|
skillPoints: number;
|
||||||
|
inventory: Inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InvItemInteraction = 'EQUIP' | 'UNEQUIP' | 'USE' | 'DROP';
|
||||||
|
export interface Inventory {
|
||||||
|
items: number[];
|
||||||
|
capacity: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PetClassInfo {
|
export interface PetClassInfo {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { PetClass } from "./Pet";
|
|
||||||
|
|
||||||
export interface PetCreationRequest {
|
export interface PetCreationRequest {
|
||||||
name: string;
|
name: string;
|
||||||
class: PetClass;
|
class: string;
|
||||||
}
|
}
|
26
src/types/Skills.ts
Normal file
26
src/types/Skills.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export interface Skill {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
type: string;
|
||||||
|
pointsCost: number;
|
||||||
|
icon: string;
|
||||||
|
skillsIdRequired: number | null;
|
||||||
|
effects: SkillEffect[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkillEffect {
|
||||||
|
id: number;
|
||||||
|
skillId: number;
|
||||||
|
tier: string;
|
||||||
|
effect: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PetSkill {
|
||||||
|
id: number;
|
||||||
|
petId: string;
|
||||||
|
skillId: number;
|
||||||
|
skill: Skill;
|
||||||
|
currentTier: string;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user