eevee/js/eevee.js
2025-08-18 19:54:03 +02:00

499 lines
18 KiB
JavaScript

const EV_NAMES=["HP", "Atk", "Def", "SpA", "SpD", "Spe"];
var mons = JSON.parse(localStorage.getItem('mons'));
if (!mons) {
mons = [];
}
var party = JSON.parse(localStorage.getItem('party'));
if (!party) {
party = [];
}
var gameData;
var opponents;
var overwriting = false;
fetch('js/game_data.json')
.then(response => response.json())
.then(data => {
setGame(data);
render_party();
})
.catch(error => console.error('Error loading JSON:', error));
fetch ('js/opponents.json')
.then(response => response.json())
.then(data => {
setOpponents(data);
render_opponents();
})
.catch(error => console.error('Error loading JSON:', error));
function setOpponents(data) {
opponents = data.mons;
}
function setGame(data) {
gameData = data;
}
function stash(i) {
party.splice(i, 1);
render_party();
}
function add_to_party(i) {
if (!party || party.length == 0) {
party = [i];
} else {
party.push(i);
}
localStorage.setItem('party', "[" + party + "]");
render_known();
}
function render_mon(mon) {
let out = "<td>" + mon.name + "</td>";
for (let ev=0; ev<6; ev++) {
let curr = mon.current_evs[ev];
let tgt = mon.target_evs[ev];
if (curr < tgt) {
out += "<td><font color=\"blue\">" + curr + "/" + tgt + "(-" + (tgt - curr) + ")" + "</font></td>";
} else if (tgt < curr) {
out += "<td><font color=\"red\">" + curr + "/" + tgt + "(+" + (curr - tgt) + ")" + "</font></td>";
} else {
out += "<td><font color=\"grey\">" + curr + "</font></td>";
}
}
out += "<td>";
if (mon.pokerus) {
out += "🦠";
}
out += "</td><td>" + render_item(mon.held_item) + "</td>";
return out;
}
function edit_mon(mon_idx, return_to_party) {
let mon = mons[mon_idx];
new_mon(return_to_party);
document.getElementById("opponents_area").hidden = true;
document.getElementById("opponent_search_field").hidden = true;
document.getElementById("name").value = mon.name;
document.getElementById("curr_hp").value = mon.current_evs[0];
document.getElementById("curr_atk").value = mon.current_evs[1];
document.getElementById("curr_def").value = mon.current_evs[2];
document.getElementById("curr_spa").value = mon.current_evs[3];
document.getElementById("curr_spd").value = mon.current_evs[4];
document.getElementById("curr_spe").value = mon.current_evs[5];
document.getElementById("tgt_hp").value = mon.target_evs[0];
document.getElementById("tgt_atk").value = mon.target_evs[1];
document.getElementById("tgt_def").value = mon.target_evs[2];
document.getElementById("tgt_spa").value = mon.target_evs[3];
document.getElementById("tgt_spd").value = mon.target_evs[4];
document.getElementById("tgt_spe").value = mon.target_evs[5];
document.getElementById("pokerus").checked = mon.pokerus;
document.getElementById("item").value = mon.held_item;
overwriting = true;
}
function new_mon(return_to_party) {
overwriting = false;
let out = "<table id='new_mon'>" +
"<tr><td><label for='name'>Name: </label></td>" +
"<td colspan='6'><input type='text' id='name'></td></tr>" +
"<tr><th></th><th>HP</th><th>Atk</th><th>Def</th><th>SpA</th><th>SpD</th><th>Spe</th></tr>" +
"<tr><td><label for='curr_hp'>Current EVs: </label></td>" +
"<td><input type='text' id='curr_hp' size=3></td>" +
"<td><input type='text' id='curr_atk' size=3></td>" +
"<td><input type='text' id='curr_def' size=3></td>" +
"<td><input type='text' id='curr_spa' size=3></td>" +
"<td><input type='text' id='curr_spd' size=3></td>" +
"<td><input type='text' id='curr_spe' size=3></td></tr>" +
"<tr><td><label for='tgt_hp'>Target EVs: </label></td>" +
"<td><input type='text' id='tgt_hp' size=3></td>" +
"<td><input type='text' id='tgt_atk' size=3></td>" +
"<td><input type='text' id='tgt_def' size=3></td>" +
"<td><input type='text' id='tgt_spa' size=3></td>" +
"<td><input type='text' id='tgt_spd' size=3></td>" +
"<td><input type='text' id='tgt_spe' size=3></td></tr>" +
"<tr><td><label for='pokerus'>Pokérus? </label></td>" +
"<td><input type='checkbox' id='pokerus'></td></tr>" +
"<tr><td><label for='item'>Held Item: </label></td>" +
"<td colspan='6'><select id='item'><option value=''>No relevant one</option>";
for (let i=0; i<gameData.adding_items.length; i++) {
let item = gameData.adding_items[i].name;
out += "<option value='" + item + "'>" + render_item(item) + "</option>";
}
for (let i=0; i<gameData.multiplying_items.length; i++) {
let item = gameData.multiplying_items[i].name;
out += "<option value='" + item + "'>" + render_item(item) + "</option>";
}
out += "</select></td></tr>" +
"</table><button onclick='try_add_mon(" + return_to_party + ")'>Validate</button>" +
"<button onclick='";
if (return_to_party) {
out += "render_party()";
} else {
out += "render_known()";
}
out += "'>Back</button>";
document.getElementById("main_area").innerHTML = out;
}
function render_item(name) {
let res = "No relevant item";
for (let i=0; i<gameData.adding_items.length; i++) {
let item = gameData.adding_items[i];
if (item.name == name) {
return name + " (" + EV_NAMES[item.ev_idx] + " +" + item.ev_added + ")";
}
}
for (let i=0; i<gameData.multiplying_items.length; i++) {
let item = gameData.multiplying_items[i];
if (item.name == name) {
return name + " (all *" + item.multiplyer + ")";
}
}
for (let i=0; i<gameData.use_items.length; i++) {
let item = gameData.use_items[i];
if (item.name == name) {
let res = name + " (" + EV_NAMES[item.ev_idx];
if (item.ev_added > 0) {
res += " +";
} else {
res += " ";
}
return res + item.ev_added + ")";
}
}
return res;
}
function feed(mon_idx) {
console.log("Feed");
let mon = mons[party[mon_idx]];
let item_name = document.getElementById('item').value;
let item;
for (let i=0; i<gameData.use_items.length; i++) {
if (item_name == gameData.use_items[i].name) {
item = gameData.use_items[i];
break;
}
}
let count = Number.parseInt(document.getElementById('count').value, 10);
if (!Number.isNaN(count)) {
let evs_added = [0, 0, 0, 0, 0, 0];
evs_added[item.ev_idx] += item.ev_added;
add_ev(mon, evs_added, count);
}
use_item(mon_idx);
}
function try_add_mon(return_to_party) {
let name = document.getElementById('name').value;
if (name == "") {
alert("Name must not be empty");
return;
}
let current_evs = [
Number.parseInt(document.getElementById('curr_hp').value, 10),
Number.parseInt(document.getElementById('curr_atk').value, 10),
Number.parseInt(document.getElementById('curr_def').value, 10),
Number.parseInt(document.getElementById('curr_spa').value, 10),
Number.parseInt(document.getElementById('curr_spd').value, 10),
Number.parseInt(document.getElementById('curr_spe').value, 10),
];
let total_curr_evs = 0;
for (let i=0; i<6; i++) {
if (Number.isNaN(current_evs[i])) {
current_evs[i] = 0;
}
if (current_evs[i] < 0 || current_evs[i] > gameData.max_ev_value) {
alert("Current EV value for " + EV_NAMES[i] + " is outside the allowed range for " + gameData.name + ": (0..=" + gameData.max_ev_value + ").");
return;
}
total_curr_evs += current_evs[i];
}
if (total_curr_evs > gameData.max_total_evs) {
alert("Total current EVs are " + total_curr_evs + ", which is higher than the maximum for " + gameData.name + ": " + gameData.max_total_evs + ".");
return;
}
let target_evs = [
Number.parseInt(document.getElementById('tgt_hp').value, 10),
Number.parseInt(document.getElementById('tgt_atk').value, 10),
Number.parseInt(document.getElementById('tgt_def').value, 10),
Number.parseInt(document.getElementById('tgt_spa').value, 10),
Number.parseInt(document.getElementById('tgt_spd').value, 10),
Number.parseInt(document.getElementById('tgt_spe').value, 10),
];
let total_tgt_evs = 0;
for (let i=0; i<6; i++) {
if (Number.isNaN(target_evs[i])) {
target_evs[i] = 0;
}
if (target_evs[i] < 0 || target_evs[i] > gameData.max_ev_value) {
alert("target EV value for " + EV_NAMES[i] + " is outside the allowed range for " + gameData.name + ": (0..=" + gameData.max_ev_value + ").");
return;
}
total_tgt_evs += target_evs[i];
}
if (total_tgt_evs > gameData.max_total_evs) {
alert("Total target EVs are " + total_tgt_evs + ", which is higher than the maximum for " + gameData.name + ": " + gameData.max_total_evs + ".");
return;
}
let pokerus = document.getElementById('pokerus').checked;
let held_item = document.getElementById('item').value;
let mon = {
name: name,
current_evs: current_evs,
target_evs: target_evs,
pokerus: pokerus,
held_item: held_item,
};
for (let i=0; i<mons.length; i++) {
if (mons[i].name == name) {
if (overwriting) {
mons[i] = mon;
localStorage.setItem('mons', JSON.stringify(mons));
if (return_to_party) {
render_party();
} else {
render_known();
}
return;
} else {
alert("You already have a Pokémon by that name. Please choose another one.");
return;
}
}
}
mons.push(mon);
localStorage.setItem('mons', JSON.stringify(mons));
if (return_to_party) {
render_party();
} else {
render_known();
}
}
function render_known() {
let out = "<h2>Known:</h2><table>";
if (mons.length == 0 || mons.length == party.length) {
out += "None";
} else if (party.length >= gameData.affected_mons) {
out += "Party is full";
} else {
out += "<tr><th>Name</th><th>HP</th><th>Atk</th><th>Def</th><th>SpA</th><th>SpD</th><th>Spe</th><th>Pokérus</th><th>Item</th></tr>";
for (let i=mons.length-1; i>=0; i--) {
if (party.includes(i)) {
continue;
}
out += "<tr>";
out += render_mon(mons[i]);
out += "<td><button onclick='add_to_party(" + i + ")'>Add</button></td>";
out += "<td><button onclick='edit_mon(" + i + ", false)'>Edit</button></td>";
out += "</tr>";
}
out += "</table>";
}
out += "<br><button onclick='new_mon()'>New</button>";
out += "<br><button onclick='render_party()'>Back</button>";
document.getElementById("main_area").innerHTML = out;
document.getElementById("opponents_area").hidden = true;
document.getElementById("opponent_search_field").hidden = true;
}
function render_party() {
let out = "<h2>Current Party:</h2><table>";
if (party.length == 0) {
out += "Empty";
} else {
out += "<tr><th>Name</th><th>HP</th><th>Atk</th><th>Def</th><th>SpA</th><th>SpD</th><th>Spe</th><th>Pokérus</th><th>Item</th></tr>";
for (let i=0; i<party.length; i++) {
out += "<tr>";
out += render_mon(mons[party[i]]);
out += "<td><button onclick='stash(" + i + ")'>Stash</button> ";
out += "<button onclick='use_item(" + i + ")'>Use Item</button> ";
out += "<button onclick='edit_mon(" + party[i] + ", true)'>Edit</button></td>";
out += "</tr>";
}
}
out += "</table>";
out += "<br><button onclick='render_known()'>Add</button> ";
out += "<button onclick='export_data()'>Export Data</button> ";
out += "<button onclick='render_import()'>Import Data</button>";
document.getElementById("main_area").innerHTML = out;
document.getElementById("opponents_area").hidden = false;
document.getElementById("opponent_search_field").hidden = false;
}
function export_data() {
let save_data = {
mons: mons,
party: party,
};
document.getElementById("opponents_area").hidden = true;
document.getElementById("opponent_search_field").hidden = true;
document.getElementById("main_area").innerHTML = "<p>Copy the contents of the following text area and save them on your device. You can paste them into the text area shown by the Import function to update your local data. Modify them in between if you like, but don't blame me if you break anything. If so, you may have to reset your browser's Local Storage.</p>" +
"<textarea id='data' type='text' style='width:100%;height:auto'></textarea><br>" +
"<button onclick='render_party()'>Back</button>";
let field = document.getElementById("data");
field.value = JSON.stringify(save_data);
field.select();
}
function render_import() {
document.getElementById("opponents_area").hidden = true;
document.getElementById("opponent_search_field").hidden = true;
document.getElementById("main_area").innerHTML = "<p>Paste your exported data into this field. If it wasn't modified, it should restore your state fine. If you did modify it, make sure it is still well-formated. If importing breaks your state, reset your browser's Local Storage and try to fix your data.</p>" +
"<textarea id='data' type='text' style='width:100%;height:auto'></textarea><br>" +
"<button onclick='try_import_data()'>Import</button>" +
"<button onclick='render_party()'>Back</button>";
}
function try_import_data () {
let save_data = JSON.parse(document.getElementById("data").value);
if (!save_data.mons) {
alert("Value for 'mons' not set");
return;
}
if (!save_data.party) {
alert("Value for 'party' not set");
return;
}
mons = save_data.mons;
party = save_data.party;
render_party();
localStorage.setItem('mons', JSON.stringify(mons));
localStorage.setItem('party', "[" + party + "]");
}
function use_item(mon_idx) {
let mon = mons[party[mon_idx]];
let out = "<h2>Currently feeding</h2><table>" +
"<tr><th>Name</th><th>HP</th><th>Atk</th><th>Def</th><th>SpA</th><th>SpD</th><th>Spe</th><th>Item</th></tr><tr>" +
render_mon(mon) + "</tr></table><table><tr><td><label for='item'>Item: </label></td>" +
"<td><select id='item'>";
for (let i=0; i<gameData.use_items.length; i++) {
let item = gameData.use_items[i].name;
out += "<option value='" + item + "'>" + render_item(item) + "</option>";
}
out += "</td></tr><tr><td><label for='count'>Count: </label></td>" +
"<td><input type='text' id='count' size=3></td></tr>" +
"<tr><td><button onclick='feed(" + mon_idx + ")'>Use Item</button></td></tr>" +
"<tr><td><button onclick='render_party()'>Back</button></td></tr></table>";
document.getElementById("main_area").innerHTML = out;
document.getElementById("opponents_area").hidden = true;
document.getElementById("opponent_search_field").hidden = true;
}
function add_ev(mon, added_evs, count) {
let curr = [...mon.current_evs];
let total = 0;
let changed = [];
for (let idx=0; idx<6; idx++) {
let new_ev = curr[idx] + added_evs[idx] * count;
if (new_ev < 0) {
new_ev = 0;
}
if (new_ev > gameData.max_ev_value) {
new_ev = gameData.max_ev_value;
}
if (new_ev != curr[idx]) {
curr[idx] = new_ev;
changed.push(idx);
}
total += new_ev;
}
if (total > gameData.max_total_evs) {
if (changed.length == 1) {
curr[changed[0]] -= total - gameData.max_total_evs;
} else {
let raised_evs = EV_NAMES[changed[0]];
for (let idx=1; idx<changed.length; idx++) {
raised_evs += ", " + EV_NAMES[changed[idx]];
}
alert("Ambiguous result trying to add " + added_evs + " to " + mon.name + ".\nTotal EVs would exceed this generation's maximum, so they must be reduced. However, it is unclear, which EV is being reduced. It could be any of:\n" + raised_evs);
return;
}
}
mon.current_evs = curr;
localStorage.setItem('mons', JSON.stringify(mons));
}
String.prototype.fuzzy = function (s) {
var hay = this.toLowerCase(), i = 0, n = -1, l;
s = s.toLowerCase();
for (; l = s[i++] ;) if (!~(n = hay.indexOf(l, n + 1))) return false;
return true;
};
function fight(opp_idx) {
if (party.length == 0) {
alert("Can't fight with an empty party. Please add some Pokémon.");
return;
}
let name = opponents[opp_idx].name;
let ev_yield = opponents[opp_idx].ev_yield;
if (confirm("Fighting " + name + ". Defeated?") == true) {
opponents.splice(opp_idx, 1);
opponents.unshift({ name: name, ev_yield: ev_yield });
for (let i=0; i<party.length; i++) {
let mon = mons[party[i]];
let evs = [...ev_yield];
let multiplyer = 1.0;
if (mon.pokerus) {
multiplyer = gameData.pokerus_multiplyer;
}
for (let j=0; j<gameData.adding_items.length;j++) {
let item = gameData.adding_items[j];
if (item.name == mon.held_item) {
evs[item.ev_idx] += item.ev_added;
break;
}
}
for (let j=0; j<gameData.multiplying_items.length;j++) {
let item = gameData.multiplying_items[j];
if (item.name == mon.held_item) {
multiplyer *= item.multiplyer;
break;
}
}
add_ev(mon, evs, multiplyer);
}
document.getElementById('opponent').value = "";
render_party();
render_opponents();
}
}
function render_opponents() {
let search_string = document.getElementById('opponent').value;
let found = [];
for (let i=0; i<opponents.length; i++) {
if (opponents[i].name.fuzzy(search_string)) {
found.push(i);
}
}
let res = "<table><tr><th>Name</th>" +
"<th>HP</th><th>Atk</th><th>Def</th><th>SpA</th><th>SpD</th><th>Spe</th>" +
"<th></th></tr>";
for (let i=0; i<found.length && i<50; i++) {
let name = opponents[found[i]].name;
let ev_yield = opponents[found[i]].ev_yield;
res += "<tr><td>" + name + "</td><td>" +
ev_yield[0] + "</td><td>" +
ev_yield[1] + "</td><td>" +
ev_yield[2] + "</td><td>" +
ev_yield[3] + "</td><td>" +
ev_yield[4] + "</td><td>" +
ev_yield[5] + "</td><td>" +
"<button onclick='fight(" + found[i] + ")'>Fight</input></td></tr>";
}
res += "</table>";
document.getElementById("opponents_area").innerHTML = res;
}