// Tweakward by Grom PE, a rebalance and tweak mod for Wayward
// v1.3a, 13 Dec 2013, for Wayward Beta 1.6
/*
== Lo and behold! ==
This mod uses monkey patching, i.e. inserting code directly into original functions.
Be very careful when using it with any other mod that uses the same mechanic, as
things can break in very unpredictable manner. It's also sensitive to Wayward updates.
To other modders: if you intend to use monkey patching, please contact me and let's
work out a good way to hook or make an API that satisfies both mods!
== Mod contents ==
GUI tweaks:
- More compact inventory dialog
- Bigger container dialog default size
- Better item tooltip
- Crafting filter allows * as input to show only currently craftable items
- Save dialog window positions and sizes correctly
- An item menu button to see what can be crafted with a particular selected item
Bugfixes:
- Grown plants don't require to be watered lots or replanted before getting fertile
Code tweaks and gameplay rebalance:
- If it says plant is not fertile, then it's not fertile
- Prevent getting many of same item on spawn
- No arrowheads generated lying around
- Quality items have a chance not to break
- Natural ground is tiring to walk on and drains stamina - make floors for good walkways
- Prevent monsters from spawning on crafted floors
- If carrying too much, drain stamina slowly according to over the top weight
- More punishment for punching trees, rocks, etc.
- Higher quality food is extra tasty
- Light items (0.1 weight) now work with container weight reduction as well
- When hit, never bleed on 90%+ health, always bleed on 30%- health
- An obligatory rare fishing bonus
- Attempting to use non-usable but equippable items equips them
- Caves are pitch black. Use torches! Which are better now, unless you swim
- Repairing depends on the tool efficiency and quality, failure damages both tool and target item
Data tweaks and gameplay rebalance:
- Halved tall grass spread
- Torch stands last for much longer
- Trout is less invincible for melee weapons, reducing the skill exploit
- Changed weight of some items
- Changed edible item effects:
- Bad and uncooked food is worse
- Onion adds thirst, cooked meat doesn't
- Water doesn't affect hunger
- Decreased durability of poor items and increased durability of iron items
- Charcoal is made only from burning logs, other items turn to ash
- Leaf bedroll is less durable and requires 10 leaves to make
- Raft and bullboat need 3 ropes, rope needs 4 string
- Twigs can be crafted from saplings and not branches
- Iron armor (refined) requires cotton fabric for padding
- Sharp rock weighs 2 and is now considered an actual rock
- Cooking a chicken isn't advanced but intermediate skill
- Sinew can be used as rope-like in crafting now
- Hammers are required to craft many tiles made from wood, stone, sandstone
- Vines in swamps can be watered and fertilized (it still takes plenty of luck to make a vine farm)
== Changelog ==
v1.3a
- Fixed grown plants disappearing
v1.3
- Updated to Wayward Beta 1.6, removed many fixes that come with it (Big thanks to Vaughn for removing the obfuscation!)
- Reduced iron hammers efficiency
- Raised exhaustion when overburdened
- Made dialog window positions and sizes save correctly
- Restored waterskin weight, now bottle is slightly lighter
- Added an item menu button to see what can be crafted with the selected item
- Tweaked the inventory window a bit more
- Fixed an error caused by ashpile or poor tongs still being in known crafting
v1.2
- Made hammers as durable as other tools
- Hammers are required to craft many tiles made from wood, stone, sandstone
- Repair efficiency varies and failure damages both tool and the target item
- Reduced stone wall weight to 16
- Reduced wooden floor and sandstone floor weights to 7 to promote making roads
- Reduced bow drill weight to 4
- Reduced chicken weight to 1
- Reduced tinder weight to 0.1
- Increased iron double axe weights to 4
- Reduced durability of wrought iron axe, shovel, pickaxe to 50
- Increased wrought iron sword durability to 40
- Subtract at least 1 stamina when encumbered
- Prevent bone from being accepted in crafting fire-making tools
- Increased cotton required to make fabric to 4
- Restored talcum powder in iron ingot recipe
- Fixed negative fertility when plants grow
- Allow vines to be farmed (but not replanted)
- Allow clay to be placed
- Removed crafting of poor tongs and ashpile
- More generic shield use-equip
- Less light without torches, stronger light from held torches
- Extinguish held torches when swimming
v1.1
- Made to work with minified (online) version of Wayward 1.5
- Small fixes
v1.0
- First public version
*/
(function()
{
"use strict";
var minified = !!$("script[src$='game.min.js']").length;
var gameStarted = player !== Player.character;
function delIfHas(arr, item)
{
var i = arr.indexOf(item);
if (i != -1) return arr.splice(i, 1);
}
// Simulate minify in a pretty naive way:
// - remove spaces
// - change 'strings' to "strings" unless there are both ' and "
// Watch out: original minify puts { after else, changes obj["stuff"] to obj.stuff
// Take care when dealing with minified code and using this function!
function minify(code)
{
code = code.replace(/(?:(\W|\w) +(\W))/g, "$1$2").replace(/(?:(\W) +(\W|\w))/g, "$1$2");
if (code.indexOf('"') == -1 || code.indexOf("'") == -1) code = code.replace(/'/g, '"');
return code;
}
// Minify, preserving string or regex type
function min_string_or_regex(e)
{
var was_regex = e instanceof RegExp;
e = minify(e.toString());
return !was_regex ? e : new RegExp(
e.substring(1, e.lastIndexOf('/')),
e.substring(e.lastIndexOf('/') + 1, e.length)
);
}
function monkeyPatch(func, replist, repargs)
{
var source = func.toString();
var args = source.substring(source.indexOf('(') + 1, source.indexOf(')'));
var body = source.substring(source.indexOf('{') + 1, source.lastIndexOf('}'));
if (replist)
{
for (var i = 0; i < replist.length; i++)
{
var debug_oldbody = body;
var rfrom = replist[i][0];
var rto = replist[i][1];
if (minified) rfrom = min_string_or_regex(rfrom);
body = body.replace(rfrom, rto);
if (debug_oldbody == body)
{
console.log("monkeyPatch: no match for '" + rfrom + "' in\n" + source);
throw new Error("monkeyPatch: couldn't find '" + rfrom + "' to replace!");
}
}
}
if (repargs) args = args.replace(repargs[0], repargs[1]);
/*jslint evil: true*/
return new Function(args, body);
}
(function guiTweaks()
{
// Compactify the inventory dialog
var e;
ui.$inventoryWindow.find("p:contains('Equipment')").remove();
e = ui.$inventoryWindow.find("p:contains('Inventory')");
e.append(" ").append(e.next().attr("placeholder", "Filter").css("width", 98));
e = ui.$inventoryWindow.find("p:contains('Crafting')");
e.append(" ").append(e.next().attr("placeholder", "Filter or *").css("width", 98));
var dialogTweaks = function()
{
ui.$inventoryWindow.parent().find("span.ui-button-text:contains('Sort Inventory')").text("Sort Inv.");
ui.$inventoryWindow.dialog("option", "minWidth", 206);
// Resize container dialog
ui.$containerWindow.dialog("option", "width", 400);
// Save dialog window sizes correctly
function fixDialogSave(el, save)
{
el.dialog("option",
{
dragStop: function()
{
var p = $(this).dialog("option", "position");
save.x = p[0];
save.y = p[1];
},
resizeStop: function()
{
var t = $(this);
save.w = t.dialog("option", "width");
save.h = t.dialog("option", "height");
}
}
);
};
fixDialogSave($('#skillswindow'), player.dialog.skillswindow);
fixDialogSave($('#milestoneswindow'), player.dialog.milestoneswindow);
fixDialogSave(ui.$inventoryWindow, player.dialog.inventorywindow);
fixDialogSave($("#messageswindow"), player.dialog.messageswindow);
fixDialogSave($('#optionswindow'), player.dialog.optionswindow);
}
// Work both when loaded ingame and when loaded as a script file
if (gameStarted) dialogTweaks();
var dialogInit_orig = dialogInit;
dialogInit = function()
{
var result = dialogInit_orig();
dialogTweaks();
return result; // Preserve return result just in case
};
ui.itemTip = monkeyPatch(ui.itemTip,
[
// Don't display double "Equip:" on armor, also add hammer efficiency info
[
'if (items[invClass].equip) {',
'if (items[invClass].repairefficiency) {' +
'var efficiency = items[invClass].repairefficiency;' +
'var quality = itemList ? itemList[invId].quality || "" : "";' +
'if (quality == "Remarkable") efficiency *= 1.03;' +
'if (quality == "Exceptional") efficiency *= 1.07;' +
'if (quality == "Legendary") efficiency *= 1.15;' +
'tip += " Repair efficiency: " + roundNumber(efficiency * 100, 0) + "%";' +
'}' +
'if (items[invClass].equip && !items[invClass].defense) {'
],
// Display "Group"/"Groups" instead of "Group(s)"
[
'/>Group(s):"',
'/>Group" + (items[invClass].group.length > 1 ? "s:" : ":")'
],
]
);
// Make * filter for immediately craftable items
// It's really ugly to use $._data(), but it's all there is
e = $._data(document.getElementById("craftfilter")).events.keyup[0];
e.handler = monkeyPatch(e.handler,
[
[
'$.each(craftItems, function () {',
'if (ui.$craftFilter.val().toLowerCase() == "*") {' +
'return $.each(craftItems, function () {' +
'var t = $(this); if (!t.hasClass("craft")) t.hide();' +
'});' +
'}' +
'$.each(craftItems, function () {',
],
]
);
})();
(function codeTweaks()
{
var i, e;
// Don't spawn monsters on tiles with nospawn
passTurn = monkeyPatch(passTurn,
[
[
'if (tile[monsterX] && tile[monsterX][monsterY]) {',
'if (tile[monsterX] && tile[monsterX][monsterY] && tiletypes[tile[monsterX][monsterY].type].nospawn) {'
],
]
);
// It's odd seeing "not fertile" plant spread the next moment
getEnvItemStatus = monkeyPatch(getEnvItemStatus,
[['if (envItems[id].spread <= 2) {', 'if (envItems[id].spread < 1) {']]
);
// Prevent getting same item twice (or more) on spawn
play = monkeyPatch(play,
[
[
'for (var item = 0; item < inventoryCount - 1; item++) {',
'{'
],
[
'itemGet(spawnItems[Math.floor(Math.random() * spawnItems.length)], "silent", "", "Random")',
'for (var item = 0; item < inventoryCount - 1; item++) {' +
'var i = Math.floor(Math.random() * spawnItems.length);' +
'itemGet(spawnItems[i], "silent", "", "Random");' +
'spawnItems.splice(i, 1);' +
'}'
],
]
);
// Arrowheads lying around is just odd
itemGen = monkeyPatch(itemGen,
[
[
'"twigs", "arrowhead", "stones"',
'"twigs", "stones"'
],
]
);
// Make quality items have a chance not to break
window.damageQualityItemCheck = function(item)
{
if (item.quality == "Remarkable")
{
// 6% chance not to break
return Math.random() < 0.94;
}
if (item.quality == "Exceptional")
{
// 15% chance not to break
return Math.random() < 0.85;
}
if (item.quality == "Legendary")
{
// 33% chance not to break
return Math.random() < 0.33333;
}
return true;
};
damageItem = monkeyPatch(damageItem,
[
[
'item.mindur -= 1',
'if (damageQualityItemCheck(item)) item.mindur -= 1'
],
]
);
Actions.attack = monkeyPatch(Actions.attack,
[
[
'ammo.mindur--',
'if (damageQualityItemCheck(ammo)) ammo.mindur--'
],
[
'weapon.mindur--',
'if (damageQualityItemCheck(weapon)) weapon.mindur--'
],
]
);
window.extinguishTorches = function()
{
var did = false;
ui.$held.find('li').each(function()
{
var invClass = $(this).attr("data-item");
var invId = $(this).attr("data-itemid");
var item = items[invClass];
if (item.onequip && item.onequip[0] === "Light Source" && item.revert)
{
player.invItems[invId].type = item.revert;
$(this).attr("data-item", item.revert);
$(this).removeClass(invClass).addClass(item.revert);
did = true;
}
}
);
if (did)
{
// No appropriate sound for that? =(
ui.message("Your torch got wet!", "bad");
equipmentStats();
}
};
move = monkeyPatch(move,
[
// Tiles with slowdown are more tiring to walk on
// No swimming with lit torches!
[
'if (player.weight > player.strength + 15) {',
'var ttt = tiletypes[tile[player.x][player.y].type];' +
'if (ttt.slowdown >= Math.random()) player.stamina--;' +
'if (ttt.water && tiletypes[tile[checkX][checkY].type].water) extinguishTorches();' +
'if (player.weight > player.strength + 15) {',
],
// Decrease stamina according to over the top weight rather than whopping 15 per turn
[
'player.stamina -= Math.round((player.weight - player.strength) / 2)',
'player.stamina -= Math.ceil((player.weight - player.strength - 15))'
],
// And also don't complain so much
[
'ui.message("You are carrying too much weight!", "bad")',
'if (window.overweightWarn) {ui.message("You are carrying too much weight!", "bad"); window.overweightWarn = true}',
],
]
);
// More punishment for punching trees, rocks, etc.
gather = monkeyPatch(gather,
[
[
'if (chance <= 10) {',
'if (chance <= 30) {'
],
[
'textAbove("-1", 255, 0, 0)',
'textAbove("-3", 255, 0, 0)'
],
[
'player.health -= 1',
'player.health -= 3',
],
]
);
Actions.eat = monkeyPatch(Actions.eat,
[
[
'var effect4 = items[invClass].onuse[3]',
'var nomnom = "";' +
'if (effect3 && player.invItems[invId].quality == "Remarkable") {' +
'skillUse += 1;' +
'nomnom = " Tasty!";' +
'}' +
'if (effect3 && player.invItems[invId].quality == "Exceptional") {' +
'skillUse += 2;' +
'nomnom = " Delicious!";' +
'}' +
'var effect4 = items[invClass].onuse[3]'
],
[
/ui\.message\('You ate\s' \+ items\[invClass\]\.name \+ '\!', 'normal'\)/, // Workaround to having space in a string...
"ui.message('You ate ' + items[invClass].name + '!' + nomnom, 'normal')"
],
]
);
// Round whole inventory weight rather than each item, making light items work with container weight reduction
craftTable = monkeyPatch(craftTable,
[
[
'player.weight += roundNumber(items[player.invItems[item].type].weight, 1)',
'player.weight += items[player.invItems[item].type].weight'
],
[
'weightReduction = roundNumber(items[player.invItems[item].container[i].type].weight * 0.25, 1)',
'weightReduction = items[player.invItems[item].container[i].type].weight * 0.25'
],
[
'player.weight += roundNumber(items[player.invItems[item].container[i].type].weight, 1) - weightReduction',
'player.weight += items[player.invItems[item].container[i].type].weight - weightReduction'
],
[
'ui.$weight.html(roundNumber(player.weight, 1) + "/" + newWeight)',
'player.weight = roundNumber(player.weight, 1);' +
'ui.$weight.html(player.weight + "/" + newWeight)'
]
]
);
// Never bleed on 90%+ health, always bleed on 30%- health
monsterMove = monkeyPatch(monsterMove,
[
[
'if (chance <= 8 - anatomyChance) {',
'var hpp = player.health / player.strength;' +
'if (hpp < 0.9 && (hpp <= 0.3 || chance <= 8 - anatomyChance)) {'
]
]
);
// Obligatory change
Actions.fishing = monkeyPatch(Actions.fishing,
[
[
'if (fishingSkill >= (chance)) {',
'if (chance == 42 && Math.random() < 0.1) {' +
'ui.message("You catch a pair of old boots!");' +
'itemGet("leatherboots", "", "", "", 1, 3);' +
'Player.skillGain("fishing", 0.1, false);' +
'other = true;' +
'} else ' +
'if (fishingSkill >= (chance)) {'
]
]
);
// Caves are pitch black. Use torches!
lighting = monkeyPatch(lighting,
[
[
'lightLevel = 0.95',
'lightLevel = 1'
],
]
);
Actions.repair = monkeyPatch(Actions.repair,
[
// Repair effect varies based on tool efficiency and quality
[
'var maxReduction = Math.floor((tileItems[tileId].maxdur - tileItems[tileId].mindur) / 2)',
'var efficiency = items[invClass].repairefficiency || 0.5;' +
'if (player.invItems[invId].quality == "Remarkable") {' +
'efficiency *= 1.03;' +
'} else if (player.invItems[invId].quality == "Exceptional") {' +
'efficiency *= 1.07;' +
'} else if (player.invItems[invId].quality == "Legendary") {' +
'efficiency *= 1.15;' +
'}' +
'var maxReduction = Math.floor((tileItems[tileId].maxdur - tileItems[tileId].mindur) * (1 - efficiency))'
],
// Break both tool and target on failure
[
'Player.skillGain(itemSkill, 0.1, false)',
'if (tileItems[tileId].maxdur > 1) tileItems[tileId].maxdur--;' +
'damageItem(invClass, invId);' +
'Player.skillGain(itemSkill, 0.1, false)'
]
]
);
// Fix negative fertility when plants grow
dynamicGrow = monkeyPatch(dynamicGrow,
[
[
'placeEnvItem(envItems[i].type, envItems[i].x + randomX, envItems[i].y + randomY, Math.floor(Math.random() * 10) - 8)',
'placeEnvItem(envItems[i].type, envItems[i].x + randomX, envItems[i].y + randomY, Math.max(Math.floor(Math.random() * 10) - 8, 0))'
]
]
);
// Add a button to see what can be crafted with an item you have
window.addModItemMenu = function(invClass, invId)
{
return '';
};
$('#itemmenu').on("mouseup", ".mod_i_crafting", function (e)
{
var p = $(this).parent();
var invClass = p.attr('data-item');
var invId = p.attr('data-itemid');
p.hide();
var knownItems = [];
for (var c in player.crafted)
{
var item = items[player.crafted[c]];
if (item.recipe && item.recipe.requires)
{
for (var r in item.recipe.requires)
{
var req = item.recipe.requires[r][0];
if (req == invClass || (items[invClass].group && items[invClass].group.indexOf(req) != -1))
{
knownItems.push(item.name);
break;
}
}
}
}
if (knownItems.length)
{
ui.message("You can use " + items[invClass].name + " to make " + knownItems.join(", ") + ".");
} else {
ui.message("You don't know what to craft with " + items[invClass].name + ".", "miss");
}
});
// It's really ugly to use $._data(), but it's all there is
for (i = 0; i < ui.$inventoryEquip.length; i++)
{
e = $._data(ui.$inventoryEquip.get(i)).events.mouseup[0];
e.handler = monkeyPatch(e.handler,
[
[
/menuButtons \+= '