Skip to content

Commit

Permalink
Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
calum committed Sep 2, 2024
1 parent 904272a commit 93ef671
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 27 deletions.
34 changes: 34 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^6.0.1",
"@mui/material": "^6.0.1",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
Expand Down
34 changes: 7 additions & 27 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,22 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<meta name="description" content="OSRS Leagues Planner - Plan and track your progress in Old School RuneScape Leagues with ease." />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>OSRS Leagues Planner</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
OSRS Leagues Planner is a tool designed to help you plan and track your progress in Old School RuneScape Leagues.
With this app, you can create, edit, and share plans that outline the steps and strategies needed to succeed in the leagues.
Whether you're looking to optimize your gameplay or simply keep track of your goals, this planner has you covered.
-->
</body>
</html>
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import StepList from './components/StepList';
import StepDetails from './components/StepDetails';
import Inventory from './components/Inventory';
import MapView from './components/MapView';
import SkillTable from './components/SkillTable';
import './App.css';
import useSteps from './hooks/useSteps';
import { handleFileUpload, handleDownloadCSV } from './utils/csvUtils';
Expand Down Expand Up @@ -80,6 +81,7 @@ function App() {
<>
<Grid container spacing={2}>
<Grid item xs={12} md={8}>
<SkillTable steps={steps} currentStep={currentStep} />
<StepDetails
step={steps[currentStep]}
onSave={handleStepUpdate}
Expand Down
205 changes: 205 additions & 0 deletions src/components/SkillTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import React, { useState, useEffect } from 'react';
import {
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Collapse, IconButton, Box, Grid
} from '@mui/material';
import { ExpandMore, ExpandLess } from '@mui/icons-material';
import TaskTracker from './TaskTracker';

const skills = [
'Attack', 'Defense', 'Strength', 'Hitpoints', 'Ranged', 'Prayer',
'Magic', 'Cooking', 'Woodcutting', 'Fletching', 'Fishing', 'Firemaking',
'Crafting', 'Smithing', 'Mining', 'Herblore', 'Agility', 'Thieving',
'Slayer', 'Farming', 'Runecrafting', 'Hunter', 'Construction'
];

const xpTable = [
{ level: 1, xp: 0 },
{ level: 2, xp: 83 },
{ level: 3, xp: 174 },
{ level: 4, xp: 276 },
{ level: 5, xp: 388 },
{ level: 6, xp: 512 },
{ level: 7, xp: 650 },
{ level: 8, xp: 801 },
{ level: 9, xp: 969 },
{ level: 10, xp: 1154 },
{ level: 11, xp: 1358 },
{ level: 12, xp: 1584 },
{ level: 13, xp: 1833 },
{ level: 14, xp: 2107 },
{ level: 15, xp: 2411 },
{ level: 16, xp: 2746 },
{ level: 17, xp: 3115 },
{ level: 18, xp: 3523 },
{ level: 19, xp: 3973 },
{ level: 20, xp: 4470 },
{ level: 21, xp: 5018 },
{ level: 22, xp: 5624 },
{ level: 23, xp: 6291 },
{ level: 24, xp: 7028 },
{ level: 25, xp: 7842 },
{ level: 26, xp: 8740 },
{ level: 27, xp: 9730 },
{ level: 28, xp: 10824 },
{ level: 29, xp: 12031 },
{ level: 30, xp: 13363 },
{ level: 31, xp: 14833 },
{ level: 32, xp: 16456 },
{ level: 33, xp: 18247 },
{ level: 34, xp: 20224 },
{ level: 35, xp: 22406 },
{ level: 36, xp: 24815 },
{ level: 37, xp: 27473 },
{ level: 38, xp: 30408 },
{ level: 39, xp: 33648 },
{ level: 40, xp: 37224 },
{ level: 41, xp: 41171 },
{ level: 42, xp: 45529 },
{ level: 43, xp: 50339 },
{ level: 44, xp: 55649 },
{ level: 45, xp: 61512 },
{ level: 46, xp: 67983 },
{ level: 47, xp: 75127 },
{ level: 48, xp: 83014 },
{ level: 49, xp: 91721 },
{ level: 50, xp: 101333 },
{ level: 51, xp: 111945 },
{ level: 52, xp: 123660 },
{ level: 53, xp: 136594 },
{ level: 54, xp: 150872 },
{ level: 55, xp: 166636 },
{ level: 56, xp: 184040 },
{ level: 57, xp: 203254 },
{ level: 58, xp: 224466 },
{ level: 59, xp: 247886 },
{ level: 60, xp: 273742 },
{ level: 61, xp: 302288 },
{ level: 62, xp: 333804 },
{ level: 63, xp: 368599 },
{ level: 64, xp: 407015 },
{ level: 65, xp: 449428 },
{ level: 66, xp: 496254 },
{ level: 67, xp: 547953 },
{ level: 68, xp: 605032 },
{ level: 69, xp: 668051 },
{ level: 70, xp: 737627 },
{ level: 71, xp: 814445 },
{ level: 72, xp: 899257 },
{ level: 73, xp: 992895 },
{ level: 74, xp: 1096278 },
{ level: 75, xp: 1210421 },
{ level: 76, xp: 1336443 },
{ level: 77, xp: 1475581 },
{ level: 78, xp: 1629200 },
{ level: 79, xp: 1798808 },
{ level: 80, xp: 1986068 },
{ level: 81, xp: 2192818 },
{ level: 82, xp: 2421087 },
{ level: 83, xp: 2673114 },
{ level: 84, xp: 2951373 },
{ level: 85, xp: 3258594 },
{ level: 86, xp: 3597792 },
{ level: 87, xp: 3972294 },
{ level: 88, xp: 4385776 },
{ level: 89, xp: 4842295 },
{ level: 90, xp: 5346332 },
{ level: 91, xp: 5902831 },
{ level: 92, xp: 6517253 },
{ level: 93, xp: 7195629 },
{ level: 94, xp: 7944614 },
{ level: 95, xp: 8771558 },
{ level: 96, xp: 9684577 },
{ level: 97, xp: 10692629 },
{ level: 98, xp: 11805606 },
{ level: 99, xp: 13034431 }
];

// Function to find the level based on XP
const getLevelFromXP = (xp) => {
for (let i = xpTable.length - 1; i >= 0; i--) {
if (xp >= xpTable[i].xp) {
return xpTable[i].level;
}
}
return 1; // Default to level 1 if no match
};

const SkillTable = ({ steps, currentStep }) => {
const [open, setOpen] = useState(false);
const [skillXP, setSkillXP] = useState({});

useEffect(() => {
const skillXPMap = {};

// Calculate XP gains from all previous tasks
for (let i = 0; i < steps.length; i++) {
steps[i].description.split('\n').forEach(line => {
skills.forEach(skill => {
const regex = new RegExp(`${skill}\\s*\\+([0-9,]+)\\s*XP`, 'i');
const match = line.match(regex);
if (match) {
const xpValue = parseInt(match[1].replace(/,/g, ''), 10);
if (!skillXPMap[skill]) {
skillXPMap[skill] = { total: 0, includingCurrent: 0 };
}
if (i < currentStep) {
skillXPMap[skill].total += xpValue;
}
if (i <= currentStep) {
skillXPMap[skill].includingCurrent += xpValue;
}
}
});
});
}

setSkillXP(skillXPMap);
}, [steps, currentStep]);

// Filter skills with XP
const skillsWithXP = Object.keys(skillXP).filter(skill => skillXP[skill].total > 0 || skillXP[skill].includingCurrent > 0);

return (
<Box mb={2}>
<Grid container spacing={1}>
<Grid item>
<IconButton onClick={() => setOpen(!open)} size="small">
{open ? <ExpandLess /> : <ExpandMore />}
<span style={{ marginLeft: '8px' }}>Skill XP Tracker</span>
</IconButton>
<Collapse in={open}>
<TableContainer component={Paper} sx={{ maxWidth: 350 }}>
<Table size="small">
<TableHead>
<TableRow>
<TableCell style={{ padding: '4px', fontSize: '0.8rem' }}>Skill</TableCell>
<TableCell style={{ padding: '4px', fontSize: '0.8rem' }}>Total XP</TableCell>
<TableCell style={{ padding: '4px', fontSize: '0.8rem' }}>Including Current</TableCell>
</TableRow>
</TableHead>
<TableBody>
{skillsWithXP.map(skill => (
<TableRow key={skill}>
<TableCell style={{ padding: '4px', fontSize: '0.8rem' }}>{skill}</TableCell>
<TableCell style={{ padding: '4px', fontSize: '0.8rem' }}>
{skillXP[skill]?.total.toLocaleString()} ({getLevelFromXP(skillXP[skill]?.total)})
</TableCell>
<TableCell style={{ padding: '4px', fontSize: '0.8rem' }}>
{skillXP[skill]?.includingCurrent.toLocaleString()} ({getLevelFromXP(skillXP[skill]?.includingCurrent)})
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Collapse>
</Grid>
<Grid item>
<TaskTracker steps={steps} currentStep={currentStep} />
</Grid>
</Grid>
</Box>
);
};

export default SkillTable;
46 changes: 46 additions & 0 deletions src/components/TaskTracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { useState, useEffect } from 'react';
import { Box, Typography } from '@mui/material';

const TaskTracker = ({ steps, currentStep }) => {
const [tasksBefore, setTasksBefore] = useState(0);
const [tasksIncluding, setTasksIncluding] = useState(0);
const [pointsBefore, setPointsBefore] = useState(0);
const [pointsIncluding, setPointsIncluding] = useState(0);

useEffect(() => {
let beforeTasks = 0;
let beforePoints = 0;
let includingTasks = 0;
let includingPoints = 0;

for (let i = 0; i <= currentStep; i++) {
const taskMatches = steps[i].description.match(/<(\d+)>/g);
if (taskMatches) {
taskMatches.forEach(match => {
const points = parseInt(match.replace(/[<>]/g, ''), 10);
includingTasks += 1;
includingPoints += points;
if (i < currentStep) {
beforeTasks += 1;
beforePoints += points;
}
});
}
}

setTasksBefore(beforeTasks);
setPointsBefore(beforePoints);
setTasksIncluding(includingTasks);
setPointsIncluding(includingPoints);
}, [steps, currentStep]);

return (
<Box sx={{ padding: '8px', border: '1px solid #ccc', borderRadius: '8px', width: '220px' }}>
<Typography variant="subtitle2">
<strong> {tasksIncluding}</strong> tasks incl. current ({pointsIncluding} pts)
</Typography>
</Box>
);
};

export default TaskTracker;

0 comments on commit 93ef671

Please sign in to comment.