Fish and locations

This commit is contained in:
Hri7566 2024-02-22 14:46:45 -05:00
parent 29676fb502
commit b23d129e8b
11 changed files with 904 additions and 5 deletions

607
config/fish.yml Normal file
View File

@ -0,0 +1,607 @@
# https://nookipedia.com/wiki/Fish
- id: "angelfish"
name: "Angelfish"
size: 10
rarity: 1
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b000011111100
- id: "arowana"
name: "Arowana"
size: 70
rarity: 1
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b000011111000
- id: "barbel_steed"
name: "Barbel steed"
size: 20
rarity: 0
location: "river"
activeMonths: 0b001000000110
- id: "bass"
name: "Bass"
size: 40
rarity: 0
location: "river"
- id: "bitterling"
name: "Bitterling"
size: 8
rarity: 0
location: "river"
activeMonths: 0b110000000011
- id: "bluegill"
name: "Bluegill"
size: 25
rarity: 0
startHour: 9
endHour: 16
- id: "carp"
name: "Carp"
size: 60
rarity: 0
location: "river"
- id: "catfish"
name: "Catfish"
size: 60
rarity: 0
location: "pond"
activeMonths: 0b000011111100
- id: "char"
name: "Char"
size: 50
rarity: 2
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b001111001110
- id: "cherry_salmon"
name: "Cherry salmon"
size: 35
rarity: 1
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b001111000000
- id: "coelacanth"
name: "Coelacanth"
size: 150
rarity: 2
location: "sea"
onlyRain: true
- id: "crucian_carp"
name: "Crucian carp"
size: 20
rarity: 0
location: "river"
- id: "dace"
name: "Dace"
size: 35
rarity: 0
location: "river"
startHour: 16
endHour: 9
- id: "eel"
name: "Eel"
size: 100
rarity: 1
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b000001111000
- id: "freshwater_goby"
name: "Freshwater goby"
size: 15
rarity: 1
location: "river"
startHour: 16
endHour: 9
- id: "giant_catfish"
name: "Giant catfish"
size: 40
rarity: 1
location: "lake"
startHour: 16
endHour: 9
activeMonths: 0b000001110000
- id: "giant_snakehead"
name: "Giant snakehead"
size: 85
rarity: 2
location: "pond"
startHour: 9
endHour: 16
- id: "goldfish"
name: "Goldfish"
size: 15
rarity: 0
location: "pond"
- id: "guppy"
name: "Guppy"
size: 4
rarity: 2
location: "river"
startHour: 9
endHour: 16
activeMonths: 0b000111111110
- id: "herabuna"
name: "Herabuna"
size: 50
rarity: 0
location: "lake"
- id: "koi"
name: "Koi"
size: 60
rarity: 1
location: "pond"
startHour: 16
endHour: 9
- id: "large_bass"
name: "Large bass"
size: 75
rarity: 1
location: "river"
- id: "loach"
name: "Loach"
size: 20
rarity: 0
location: "river"
activeMonths: 0b001110000000
- id: "pale_chub"
name: "Pale chub"
size: 15
rarity: 1
location: "river"
- id: "piranha"
name: "Piranha"
size: 30
rarity: 2
location: "river"
startHour: 9
endHour: 16
activeMonths: 0b000001111000
- id: "pond_smelt"
name: "Pond smelt"
size: 15
rarity: 0
location: "river"
activeMonths: 0b110000000001
- id: "pop-eyed_goldfish"
name: "Pop-eyed goldfish"
size: 15
rarity: 1
location: "pond_smelt"
startHour: 9
endHour: 4
- id: "rainbow_trout"
name: "Rainbow trout"
size: 60
rarity: 0
location: "river"
startHour: 4
endHour: 21
activeMonths: 0b001111001110
- id: "salmon"
name: "Salmon"
size: 90
rarity: 0
location: "river"
activeMonths: 0b000000001000
- id: "small_bass"
name: "Small bass"
size: 30
rarity: 0
location: "river"
- id: "stringfish"
name: "Stringfish"
size: 140
rarity: 2
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b111000000001
- id: "sweetfish"
name: "Sweetfish"
size: 25
rarity: 1
location: "river"
activeMonths: 0b000000111000
- id: "arapaima"
name: "Arapaima"
size: 300
rarity: 2
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b000001111000
- id: "barred_knifejaw"
name: "Barred knifejaw"
size: 60
rarity: 1
location: "sea"
activeMonths: 0b001111111110
- id: "brook_trout"
name: "Brook trout"
size: 50
rarity: 0
location: "lake"
- id: "crawfish"
name: "Crawfish"
size: 12
rarity: 0
location: "pond"
activeMonths: 0b000111111000
- id: "frog"
name: "Frog"
size: 12
rarity: 0
location: "pond"
activeMonths: 0b000011110000
- id: "jellyfish"
name: "Jellyfish"
size: 25
rarity: 1
location: "sea"
activeMonths: 0b000000111000
- id: "moon_jellyfish"
name: "Moon jellyfish"
size: 30
rarity: 1
location: "sea"
activeMonths: 0b000001111000
- id: "killifish"
name: "Killifish"
size: 4
rarity: 1
location: "pond"
activeMonths: 0b000111110000
- id: "red_snapper"
name: "Red snapper"
size: 90
rarity: 1
location: "sea"
- id: "sea_bass"
name: "Sea bass"
size: 100
rarity: 0
location: "sea"
- id: "blue_marlin"
name: "Blue marlin"
size: 220
rarity: 2
location: "sea" # "pier"
activeMonths: 0b111100111011
- id: "dab"
name: "Dab"
size: 50
rarity: 1
location: "sea"
activeMonths: 0b111100000111
- id: "horse_mackerel"
name: "Horse mackerel"
size: 40
rarity: 0
location: "sea"
- id: "octopus"
name: "Octopus"
size: 50
rarity: 0
location: "sea"
- id: "olive_flounder"
name: "Olive flounder"
size: 80
rarity: 0
location: "sea"
- id: "puffer_fish"
name: "Puffer fish"
size: 35
rarity: 1
location: "sea"
- id: "sea_horse"
name: "Sea horse"
size: 8
rarity: 0
location: "sea"
activeMonths: 0b000111111110
- id: "squid"
name: "Squid"
size: 35
rarity: 0
location: "sea"
activeMonths: 0b111111110001
- id: "black_bass"
name: "Black bass"
size: 50
rarity: 0
location: "river"
- id: "clown_fish"
name: "Clown fish"
size: 15
rarity: 0
location: "sea"
activeMonths: 0b000111111000
- id: "dorado"
name: "Dorado"
size: 100
rarity: 2
location: "river"
activeMonths: 0b000001111000
- id: "football_fish"
name: "Football fish"
size: 60
rarity: 1
startHour: 16
endHour: 9
location: "sea"
- id: "gar"
name: "Gar"
size: 190
rarity: 1
location: "pond"
startHour: 16
endHour: 9
activeMonths: 0b000001111000
- id: "great_white_shark"
name: "Great white shark"
size: 540
rarity: 2
location: "sea"
startHour: 16
endHour: 9
activeMonths: 0b000001111000
- id: "hammerhead_shark"
name: "Hammerhead shark"
size: 250
rarity: 2
location: "sea"
startHour: 16
endHour: 9
activeMonths: 0b000001111000
- id: "king_salmon"
name: "King salmon"
size: 140
rarity: 1
location: "river"
activeMonths: 0b000000001000
- id: "ocean_sunfish"
name: "Ocean sunfish"
size: 300
rarity: 2
location: "sea"
startHour: 4
endHour: 21
- id: "sea_butterfly"
name: "Sea butterfly"
size: 3
rarity: 0
location: "sea"
activeMonths: 0b111000000001
- id: "tuna"
name: "Tuna"
size: 230
rarity: 2
location: "sea"
activeMonths: 0b111100000011
- id: "yellow_perch"
name: "Yellow perch"
size: 35
rarity: 0
location: "river"
activeMonths: 0b111000001111
- id: "zebra_turkeyfish"
name: "Zebra turkeyfish"
size: 30
rarity: 0
location: "sea"
activeMonths: 0b111100000011
- id: "butterfly_fish"
name: "Butterfly fish"
size: 18
rarity: 1
location: "sea"
activeMonths: 0b111100001111
- id: "lobster"
name: "Lobster"
size: 50
rarity: 2
location: "sea"
activeMonths: 0b100111000001
- id: "mooray_eel"
name: "Mooray eel"
size: 80
rarity: 1
location: "sea"
activeMonths: 0b000000011100
- id: "napoleanfish"
name: "Napoleanfish"
size: 180
rarity: 2
location: "sea"
startHour: 4
endHour: 21
activeMonths: 0b000000110000
- id: "neon_tetra"
name: "Neon tetra"
size: 2
rarity: 1
location: "river"
startHour: 9
endHour: 16
activeMonths: 0b000111111110
- id: "pike"
name: "Pike"
size: 120
rarity: 1
location: "river"
activeMonths: 0b000000001111
- id: "ray"
name: "Ray"
size: 120
rarity: 1
location: "sea"
startHour: 4
endHour: 21
activeMonths: 0b000000011110
- id: "surgeonfish"
name: "Surgeonfish"
size: 31
rarity: 1
location: "sea"
activeMonths: 0b000111111000
- id: "blowfish"
name: "Blowfish"
size: 25
rarity: 1
location: "sea"
startHour: 21
endHour: 4
activeMonths: 0b110000000011
- id: "giant_trevally"
name: "Giant trevally"
size: 180
rarity: 2
location: "sea"
activeMonths: 0b000011111100
- id: "mitten_crab"
name: "Mitten crab"
size: 8
rarity: 2
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b000000001110
- id: "nibble_fish"
name: "Nibble fish"
size: 8
rarity: 2
location: "river"
startHour: 9
endHour: 16
activeMonths: 0b000011111000
- id: "oarfish"
name: "Oarfish"
size: 580
rarity: 2
location: "sea"
activeMonths: 0b111110000001
- id: "ribbon_eel"
name: "Ribbon eel"
size: 120
rarity: 1
location: "sea"
activeMonths: 0b000001111100
- id: "saddled_bichir"
name: "Saddled bichir"
size: 60
rarity: 1
location: "river"
startHour: 21
endHour: 4
activeMonths: 0b000001111000
- id: "saw_shark"
name: "Saw shark"
size: 150
rarity: 2
location: "sea"
startHour: 16
endHour: 9
activeMonths: 0b000001111000
- id: "soft-shelled_turtle"
name: "Soft-shelled Turtle"
size: 30
rarity: 1
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b000000011000
- id: "tadpole"
name: "Tadpole"
size: 4
rarity: 0
location: "pond"
activeMonths: 0b001111100000
- id: "whale_shark"
name: "Whale shark"
size: 600
rarity: 2
location: "sea"
activeMonths: 0b000001111000
- id: "anchovy"
name: "Anchovy"
size: 15
rarity: 1
location: "sea"
startHour: 4
endHour: 21
- id: "barreleye"
name: "Barreleye"
size: 20
rarity: 2
location: "sea"
startHour: 21
endHour: 4
- id: "betta"
name: "Betta"
size: 7
rarity: 1
location: "river"
startHour: 9
endHour: 16
activeMonths: 0b000011111100
- id: "golden_trout"
name: "Golden trout"
size: 30
rarity: 2
location: "river"
startHour: 16
endHour: 9
activeMonths: 0b001110001110
- id: "mahi-mahi"
name: "Mahi-mahi"
size: 110
rarity: 2
location: "sea"
activeMonths: 0b000011111100
- id: "rainbowfish"
name: "Rainbowfish"
size: 12
rarity: 1
location: "river"
startHour: 9
endHour: 16
activeMonths: 0b000011111100
- id: "ranchu_goldfish"
name: "Ranchu goldfish"
size: 15
rarity: 1
location: "pond"
startHour: 9
endHour: 16
- id: "snapping_turtle"
name: "Snapping turtle"
size: 50
rarity: 0
location: "river"
startHour: 21
endHour: 4
activeMonths: 0b000111111100
- id: "sturgeon"
name: "Sturgeon"
size: 20
rarity: 2
location: "river"
activeMonths: 0b111000001111
- id: "suckerfish"
name: "Suckerfish"
size: 110
rarity: 2
location: "sea"
activeMonths: 0b000001111000
- id: "tilapia"
name: "Tilapia"
size: 20
rarity: 0
location: "river"
activeMonths: 0b000001111100

20
config/locations.yml Normal file
View File

@ -0,0 +1,20 @@
- id: pond
name: Pond
nearby: []
hasSand: true
objects: []
- id: lake
name: Lake
nearby: []
hasSand: false
objects: []
- id: river
name: River
nearby: []
hasSand: false
objects: []
- id: sea
name: Sea
nearby: []
hasSand: true
objects: []

View File

@ -20,14 +20,15 @@ model User {
} }
model Inventory { model Inventory {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
balance Int @default(0) balance Int @default(0)
location String @default("pond")
items Json @default("[]") items Json @default("[]")
fishSack Json @default("[]") fishSack Json @default("[]")
pokemon Json @default("[]") pokemon Json @default("[]")
User User? user User?
} }
model AuthToken { model AuthToken {

32
scripts/numfish.ts Normal file
View File

@ -0,0 +1,32 @@
import { Logger } from "@util/Logger";
import { argv } from "bun";
import { existsSync, readFileSync, writeFileSync } from "fs";
import YAML from "yaml";
const logger = new Logger("Numfish");
const inFile = argv[2];
if (typeof inFile !== "string") {
logger.error(`Usage: <infile>`);
process.exit();
}
if (!existsSync(inFile)) {
logger.error("Input file not found");
process.exit();
}
logger.info("Reading YAML...");
let data: IFish[];
try {
const ydata = readFileSync(inFile).toString();
data = YAML.parse(ydata);
logger.info("Number of fish:", data.length);
} catch (err) {
logger.error(err);
logger.error("YAML read error");
process.exit();
}

15
scripts/rfish.ts Normal file
View File

@ -0,0 +1,15 @@
import { randomFish } from "@server/fish/fish";
import { Logger } from "@util/Logger";
import { argv } from "bun";
const logger = new Logger("Numfish");
const location = argv[2];
if (typeof location !== "string") {
logger.error(`Usage: <location>`);
process.exit();
}
const fish = randomFish(location);
logger.info(fish);

View File

@ -0,0 +1,34 @@
import Command from "@server/commands/Command";
import { getInventory } from "@server/data/inventory";
import { locations } from "@server/fish/locations";
const answers = [
"You are at {loc}.",
"You appear to be at {loc}.",
"According to your map, you are at {loc}.",
"The map says you are at {loc}.",
"Looking at your world atlas, you appear to be at {loc}.",
"The world atlas defines your location as {loc}.",
"Judging by the wind direction, hemisphere sun angle, and the size of the waves in the water, you deduce that you must be at {loc}.",
"You run your fingers down the map until you find {loc}. This is where you must be.",
"Your cell phone's maps app shows your location as {loc}.",
"You pull your cell phone out of your pocket and turn it on. Opening the maps app, you realize you are at {loc}."
];
export const location = new Command(
"location",
["location", "loc", "where", "whereami", "l"],
"Get current location",
"location",
"command.fishing.location",
async ({ id, command, args, prefix, part, user }) => {
const inventory = await getInventory(user.inventoryId);
if (!inventory) return;
const loc = locations.find(loc => loc.id == inventory.location);
if (!loc) return "You are in the middle of nowhere.";
const answer = answers[Math.floor(Math.random() * answers.length)];
return answer.replaceAll("{loc}", loc.name);
}
);

View File

@ -3,6 +3,7 @@ import { fish } from "./fishing/fish";
import { help } from "./general/help"; import { help } from "./general/help";
import { setcolor } from "./util/setcolor"; import { setcolor } from "./util/setcolor";
import { data } from "./util/data"; import { data } from "./util/data";
import { location } from "./fishing/location";
interface ICommandGroup { interface ICommandGroup {
id: string; id: string;
@ -23,7 +24,7 @@ commandGroups.push(general);
const fishing: ICommandGroup = { const fishing: ICommandGroup = {
id: "fishing", id: "fishing",
displayName: "Fishing", displayName: "Fishing",
commands: [fish] commands: [fish, location]
}; };
commandGroups.push(fishing); commandGroups.push(fishing);

103
src/api/fish/fish.ts Normal file
View File

@ -0,0 +1,103 @@
import { Logger } from "@util/Logger";
import { loadConfig } from "@util/config";
export const fish = loadConfig<IFish[]>("config/fish.yml", []);
const logger = new Logger("Fishies");
export function randomFish(location: string, r: number = Math.random()) {
let rarity = 0;
// logger.debug("R:", r);
if (r < 0.1) {
rarity = 2;
} else if (r < 0.6) {
rarity = 1;
} else if (r < 1) {
rarity = 0;
}
let animal: IFish | undefined;
let matchesLocation = false;
let matchesRarity = false;
let matchesMonth = false;
let matchesTime = false;
let maxRecursion = 1000;
let i = 0;
while (
!matchesLocation ||
!matchesRarity ||
!matchesMonth ||
!matchesTime
) {
if (i >= maxRecursion) {
break;
}
animal = fish[Math.floor(Math.random() * fish.length)];
matchesLocation = animal.location == location;
matchesRarity = animal.rarity == rarity;
matchesMonth =
typeof animal.activeMonths !== "undefined"
? hasFishMonths(animal.activeMonths)
: true;
matchesTime =
typeof animal.startHour !== "undefined" &&
typeof animal.endHour !== "undefined"
? hasFishTime(animal.startHour, animal.endHour)
: true;
// logger.debug("Matches location:", matchesLocation);
// logger.debug("Matches rarity:", matchesRarity);
// logger.debug("Matches month:", matchesMonth);
// logger.debug("Matches time:", matchesTime);
i++;
}
return animal as IFish;
}
export function hasFishMonths(activeMonths: string, t: number = Date.now()) {
const d = new Date(t);
const month = d.getMonth();
const bin = 0x800 >> month;
const active = parseInt(activeMonths.substring(2), 2);
// logger.debug("Current month: ", bin.toString(2).padStart(12, "0"));
// logger.debug("Active: ", active.toString(2).padStart(12, "0"));
// logger.debug(
// "Bitwise: ",
// (bin & active).toString(2).padStart(12, "0")
// );
return !!(bin & active);
}
export function hasFishTime(
startHour: number,
endHour: number,
t: number = Date.now()
) {
const d = new Date(t);
const hour = d.getHours();
if (startHour < endHour) {
if (hour > startHour && hour < endHour) {
return true;
}
} else {
if (hour > startHour || hour < startHour) {
return true;
}
}
return false;
}

56
src/api/fish/locations.ts Normal file
View File

@ -0,0 +1,56 @@
import { loadConfig } from "@util/config";
export const locations = loadConfig<ILocation[]>("config/locations.yml", [
{
id: "pond",
name: "Pond",
nearby: [],
hasSand: true,
objects: []
},
{
id: "lake",
name: "Lake",
nearby: [],
hasSand: false,
objects: []
},
{
id: "river",
name: "River",
nearby: [],
hasSand: false,
objects: []
},
{
id: "sea",
name: "Sea",
nearby: [],
hasSand: true,
objects: []
}
]);
const sand: IItem = {
id: "sand",
name: "Sand"
};
let sandInterval: Timer;
export function startSandInterval() {
sandInterval = setInterval(() => {
for (const loc of locations) {
if (!loc.hasSand) continue;
let existing = loc.objects.find(obj => obj.id == "sand");
if (typeof existing !== "undefined") continue;
loc.objects.push(sand);
}
}, 6000);
}
export function stopSandInterface() {
clearInterval(sandInterval);
}

View File

@ -1,2 +1,5 @@
import "./api/server"; import "./api/server";
import "./cli/readline"; import "./cli/readline";
import { startSandInterval } from "./fish/locations";
startSandInterval();

29
src/util/types.d.ts vendored
View File

@ -21,10 +21,24 @@ interface CountComponent {
count: number; count: number;
} }
interface IItem {
id: string;
name: string;
}
interface IFish extends JsonValue { interface IFish extends JsonValue {
id: string; id: string;
name: string; name: string;
size: string; size: number;
rarity: number;
location: string;
onlyRain?: true;
startHour?: number;
endHour?: number;
activeMonths?: string;
emoji?: string;
} }
interface IPokemon extends JsonValue { interface IPokemon extends JsonValue {
@ -46,14 +60,19 @@ interface IPokemon extends JsonValue {
}; };
} }
type TInventoryItems = JsonArray & IItem[];
type TFishSack = JsonArray & IFish[]; type TFishSack = JsonArray & IFish[];
type TPokemonSack = JsonArray & IPokemon[]; type TPokemonSack = JsonArray & IPokemon[];
interface IInventory { interface IInventory {
id: number; id: number;
balance: number; balance: number;
items: TInventoryItems;
fishSack: TFishSack; fishSack: TFishSack;
pokemon: TPokemonSack; pokemon: TPokemonSack;
user: User;
} }
interface IBack<T extends string | unknown> extends Record<string, unknown> { interface IBack<T extends string | unknown> extends Record<string, unknown> {
@ -65,3 +84,11 @@ interface Backs extends Record<string, IBack<unknown>> {
m: "color"; m: "color";
}; };
} }
interface ILocation {
id: string;
name: string;
nearby: string[];
objects: (IItem | IFish | IPokemon)[];
hasSand: boolean;
}