Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,16 @@ The server runs on the 435 revision of the game, which was a game update made on
* Formula for success :heavy_check_mark:
* Chopping Trees :heavy_check_mark:
* Axes :heavy_check_mark:
* Birds nests :x:
* Birds nests :heavy_check_mark:
* Stump ids :yellow_square:
* Canoes :x:
* Mining
* Formula for success :heavy_check_mark:
* Mining ores :heavy_check_mark:
* Pickaxes :heavy_check_mark:
* Random gems :x:
* Gem ores :x:
* Essence mining :x:
* Random gems :heavy_check_mark:
* Gem ores :heavy_check_mark:
* Essence mining :heavy_check_mark:
* Empty Rock ids :yellow_square:
* Crafting
* Spinning wheel :heavy_check_mark:
Expand Down
16 changes: 16 additions & 0 deletions src/util/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ export function hexToRgb(hex: number): { r: number, b: number, g: number } {
};
}

export function hexToHexString(hex: number): string {
let r = ((hex >> 16) & 0xff).toString(16);
let g = ((hex >> 8) & 0xff).toString(16);
let b = (hex & 0xff).toString(16);
if (r === '0') {
r = '00';
}
if (g === '0') {
g = '00';
}
if (b === '0') {
b = '00';
}
return r + g + b;
}

export function rgbTo16Bit(r: number, g: number, b: number): number {
return ((r & 0x1f) << 11) | ((g & 0x3f) << 5) | (b & 0x1f) << 0;
}
Expand Down
31 changes: 19 additions & 12 deletions src/util/strings.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { hexToHexString } from '@server/util/colors';

export const startsWithVowel = (str: string): boolean => {
str = str.trim().toLowerCase();

Expand All @@ -7,7 +9,7 @@ export const startsWithVowel = (str: string): boolean => {
};

// Thank you to the Apollo team for these values. :)
const charWidths = [ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
const charWidths = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 7, 14, 9, 12, 12, 4, 5,
5, 10, 8, 4, 8, 4, 7, 9, 7, 9, 8, 8, 8, 9, 7, 9, 9, 4, 5, 7,
9, 7, 9, 14, 9, 8, 8, 8, 7, 7, 9, 8, 6, 8, 8, 7, 10, 9, 9, 8,
Expand All @@ -19,7 +21,7 @@ const charWidths = [ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
8, 8, 4, 5, 5, 6, 7, 11, 11, 11, 9, 9, 9, 9, 9, 9, 9, 13, 8, 8,
8, 8, 8, 4, 4, 5, 4, 8, 9, 9, 9, 9, 9, 9, 8, 10, 9, 9, 9, 9,
8, 8, 8, 8, 8, 8, 8, 8, 8, 13, 6, 8, 8, 8, 8, 4, 4, 5, 4, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ];
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8];

export function wrapText(text: string, maxWidth: number): string[] {
const lines = [];
Expand All @@ -30,11 +32,11 @@ export function wrapText(text: string, maxWidth: number): string[] {
let widthAfterSpace = 0;
let lastSpaceChar = '';

for(let i = 0; i < text.length; i++) {
for (let i = 0; i < text.length; i++) {
const char = text.charAt(i);

// Ignore <col=> and </col> strings...
if(char === '<' && (text.charAt(i + 1) === '/' || text.charAt(i + 1) === 'c' && text.charAt(i + 2) === 'o' && text.charAt(i + 3) === 'l')) {
if (char === '<' && (text.charAt(i + 1) === '/' || text.charAt(i + 1) === 'c' && text.charAt(i + 2) === 'o' && text.charAt(i + 3) === 'l')) {
const tagCloseIndex = text.indexOf('>', i);
i = tagCloseIndex;
continue;
Expand All @@ -44,20 +46,20 @@ export function wrapText(text: string, maxWidth: number): string[] {
width += charWidth;
widthAfterSpace += charWidth;

if(char === ' ' || char === '\n' || char === '-') {
if (char === ' ' || char === '\n' || char === '-') {
lastSpaceChar = char;
lastSpace = i;
widthAfterSpace = 0;
}

if(width >= maxWidth || char === '\n') {
if (width >= maxWidth || char === '\n') {
lines.push(text.substring(lineStartIdx, lastSpaceChar === '-' ? lastSpace + 1 : lastSpace));
lineStartIdx = lastSpace + 1;
width = widthAfterSpace;
}
}

if(lineStartIdx !== text.length - 1) {
if (lineStartIdx !== text.length - 1) {
lines.push(text.substring(lineStartIdx, text.length));
}

Expand All @@ -67,14 +69,19 @@ export function wrapText(text: string, maxWidth: number): string[] {
export function stringToLong(s: string): bigint {
let l: bigint = BigInt(0);

for(let i = 0; i < s.length && i < 12; i++) {
for (let i = 0; i < s.length && i < 12; i++) {
const c = s.charAt(i);
const cc = s.charCodeAt(i);
l *= BigInt(37);
if(c >= 'A' && c <= 'Z') l += BigInt((1 + cc) - 65);
else if(c >= 'a' && c <= 'z') l += BigInt((1 + cc) - 97);
else if(c >= '0' && c <= '9') l += BigInt((27 + cc) - 48);
if (c >= 'A' && c <= 'Z') l += BigInt((1 + cc) - 65);
else if (c >= 'a' && c <= 'z') l += BigInt((1 + cc) - 97);
else if (c >= '0' && c <= '9') l += BigInt((27 + cc) - 48);
}
while(l % BigInt(37) == BigInt(0) && l != BigInt(0)) l /= BigInt(37);
while (l % BigInt(37) == BigInt(0) && l != BigInt(0)) l /= BigInt(37);
return l;
}

export function colorText(s: string, hexColor: number): string {
console.log(hexToHexString(hexColor));
return `<col=${hexToHexString(hexColor)}>${s}</col>`;
}
14 changes: 13 additions & 1 deletion src/world/config/harvestable-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ export enum Ore {
GOLD,
MITHIL,
ADAMANT,
RUNITE
RUNITE,
RUNE_ESS,
GEM
}


Expand Down Expand Up @@ -223,6 +225,16 @@ const Ores: IHarvestable[] = [
respawnHigh: 1200,
baseChance: -10,
break: 100
},
{
objects: new Map<number, number>([[2111, 450]]), // Gem rocks
itemId: 1625,
level: 40,
experience: 65.0,
respawnLow: 200,
respawnHigh: 400,
baseChance: 30,
break: 100
}
];

Expand Down
11 changes: 11 additions & 0 deletions src/world/skill-util/glory-boost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Player } from '@server/world/actor/player/player';

export function checkForGemBoost(player: Player): number {
if (player.hasItemInEquipment(1706) ||
player.hasItemInEquipment(1708) ||
player.hasItemInEquipment(1710) ||
player.hasItemInEquipment(1712)) {
return 86;
}
return 256;
}
59 changes: 59 additions & 0 deletions src/world/skill-util/harvest-roll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Note if adding hunter, Strung rabbit foot makes this out of 94 instead of 99
import { randomBetween } from '@server/util/num';
import { Item } from '@server/world/items/item';

export function rollBirdsNestType(): Item {
const roll = randomBetween(0, 99);
let itemId;
if (roll > 3) {
// Bird egg
if (roll === 0) {
itemId = 5070; // Red egg
} else if (roll === 1) {
itemId = 5071; // Green egg
} else {
itemId = 5072; // blue egg
}
} else if (roll > 34) {
itemId = 5074; // Ring
} else {
itemId = 5073; // Seeds
}
return {itemId: itemId, amount: 1};
}

export function rollGemType(): Item {
const roll = randomBetween(0, 3);
let itemId;
if (roll === 0) {
itemId = 1617; // Uncut Diamond
} else if (roll === 1) {
itemId = 1619; // Uncut ruby
} else if (roll === 2) {
itemId = 1621; // uncut emerald
} else {
itemId = 1623; // uncut sapphire
}
return {itemId: itemId, amount: 1};
}

export function rollGemRockResult(): Item {
const roll = randomBetween(0, 127);
let itemId;
if (roll < 60) {
itemId = 1625; // Uncut Opal
} else if (roll < 90) {
itemId = 1627; // Uncut Jade
} else if (roll < 105) {
itemId = 1629; // uncut topaz
} else if (roll < 114) {
itemId = 1623; // uncut sapphire
} else if (roll < 119) {
itemId = 1621; // uncut emerald
} else if (roll < 124) {
itemId = 1619; // uncut ruby
} else {
itemId = 1617; // uncut diamond
}
return {itemId: itemId, amount: 1};
}
47 changes: 40 additions & 7 deletions src/world/skill-util/harvest-skill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { loopingAction } from '@server/world/actor/player/action/action';
import { randomBetween } from '@server/util/num';
import { ObjectActionDetails } from '@server/world/actor/player/action/object-action';
import { colors } from '@server/util/colors';
import { checkForGemBoost } from '@server/world/skill-util/glory-boost';
import { colorText } from '@server/util/strings';
import { rollBirdsNestType, rollGemRockResult, rollGemType } from '@server/world/skill-util/harvest-roll';

export function canInitiateHarvest(player: Player, target: IHarvestable, skill: Skill): undefined | HarvestTool {
if (!target) {
Expand All @@ -16,7 +19,7 @@ export function canInitiateHarvest(player: Player, target: IHarvestable, skill:
player.sendMessage('There is current no ore available in this rock.');
break;
default:
player.sendMessage(`<col=${colors.red}>HARVEST SKILL ERROR, PLEASE CONTACT DEVELOPERS</col>`);
player.sendMessage(colorText('HARVEST SKILL ERROR, PLEASE CONTACT DEVELOPERS', colors.red));
break;


Expand Down Expand Up @@ -78,7 +81,15 @@ export function canInitiateHarvest(player: Player, target: IHarvestable, skill:
}

export function handleHarvesting(details: ObjectActionDetails, tool: HarvestTool, target: IHarvestable, skill: Skill): void {
let targetName: string = cache.itemDefinitions.get(target.itemId).name.toLowerCase();
let itemToAdd = target.itemId;
if (itemToAdd === 1436 && details.player.skills.hasLevel(Skill.MINING, 30)) {
itemToAdd = 7936;
}
if (details.object.objectId === 2111 && details.player.skills.hasLevel(Skill.MINING, 30)) {
itemToAdd = rollGemRockResult().itemId;
}
let targetName: string = cache.itemDefinitions.get(itemToAdd).name.toLowerCase();

switch (skill) {
case Skill.MINING:
targetName = targetName.replace(' ore', '');
Expand Down Expand Up @@ -110,19 +121,41 @@ export function handleHarvesting(details: ObjectActionDetails, tool: HarvestTool
toolLevel = 2;
}
const percentNeeded = target.baseChance + toolLevel + details.player.skills.values[skill].level;
details.player.sendMessage(`roll: ${successChance}, needed: ${percentNeeded}`);
if (successChance <= percentNeeded) {
if (details.player.inventory.hasSpace()) {

let randomLoot = false;
let roll = 0;
switch (skill) {
case Skill.MINING:
details.player.sendMessage(`You manage to mine some ${targetName}.`);
roll = randomBetween(1, checkForGemBoost(details.player));
if (roll === 1) {
randomLoot = true;
details.player.sendMessage(colorText('You found a rare gem.', colors.red));
details.player.giveItem(rollGemType());
}
break;
case Skill.WOODCUTTING:
details.player.sendMessage(`You manage to chop some ${targetName}.`);
roll = randomBetween(1, 256);
if (roll === 1) {
randomLoot = true;
details.player.sendMessage(colorText('A bird\'s nest falls out of the tree.', colors.red));
world.spawnWorldItem(rollBirdsNestType(), details.player.position, details.player, 300);
}
break;
}
details.player.giveItem(target.itemId);
if (!randomLoot) {
switch (skill) {
case Skill.MINING:
details.player.sendMessage(`You manage to mine some ${targetName}.`);
break;
case Skill.WOODCUTTING:
details.player.sendMessage(`You manage to chop some ${targetName}.`);
break;
}

details.player.giveItem(itemToAdd);

}
details.player.skills.addExp(skill, target.experience);
if (randomBetween(0, 100) <= target.break) {
details.player.playSound(soundIds.oreDepeleted);
Expand Down