Created
November 11, 2020 15:05
-
-
Save Suor/966c29ee15d9b2b7b04e404aba85b735 to your computer and use it in GitHub Desktop.
Logger pretty printer in Squirrel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
::mods_registerMod("mod_standout_foes", 0.1, "Standout foes"); | |
local gt = this.getroottable(); | |
// Alias to make it easier for us inside. Things are still global and accessible from outside, | |
// so if anyone will want to write a mod for this mod then it should be easy enough. | |
local sf = gt.StandoutFoes <- {}; | |
local Debug = gt.StandoutFoes.Debug <- {}; | |
gt.StandoutFoes.Quirks <- { | |
fast = { | |
prefix = "Fast", | |
XPMult = 1.2, | |
function apply(e) { | |
// More action points and initiave | |
e.m.ActionPoints += 2; | |
e.m.BaseProperties.ActionPoints += 2; | |
e.m.BaseProperties.Initiative += 25; | |
// TODO: vary chance on scale/diff? | |
// Give adrenaline probably | |
if (sf.Rand.chance(0.5)) { | |
e.m.Skills.add(this.new("scripts/skills/perks/perk_adrenalin")); | |
e.m.AIAgent.addBehavior(this.new("scripts/ai/tactical/behaviors/ai_adrenaline")); | |
} | |
// TODO: give dodge? | |
// More action points plus adrenaline drains stamina so we add some | |
e.m.BaseProperties.Stamina += 25; | |
e.m.BaseProperties.FatigueRecoveryRate += 5; | |
// Being fast helps hit and not being hit | |
sf.Util.tweakOffence(e, 8); | |
sf.Util.tweakDefence(e, 5); | |
// Slightly red head | |
sf.Util.color(e, "head", "#ffdddd", 0.9); | |
} | |
}, | |
big = { | |
prefix = "Big", | |
XPMult = 1.2, | |
function apply(e) { | |
sf.Util.tweakOffence(e, 0, 1.25); | |
sf.Util.tweakDefence(e, -5, 1.7); | |
sf.Util.scale(e, 1.12); | |
} | |
} | |
} | |
sf.partyStats <- function(party) { | |
this.logInfo("sf: partyStats " + party.getName()); | |
local stats = { | |
factionType = this.World.FactionManager.getFaction(party.getFaction()).getType(), | |
size = party.m.Troops.len(), | |
} | |
return stats; | |
} | |
// Quirk helpers | |
gt.StandoutFoes.Util <- { | |
function applyQuirk(e, quirk) { | |
this.logInfo("Apply " + quirk.prefix + " to " + e.getName()); | |
e.m.Name = quirk.prefix + " " + e.m.Name; | |
e.m.XP *= quirk.XPMult; | |
quirk.apply(e); | |
} | |
function tweakOffence(e, skill, damageMult = 1.0) { | |
local b = e.m.BaseProperties; | |
b.MeleeSkill += skill; | |
b.RangedSkill += skill; | |
b.DamageTotalMult *= damageMult; | |
} | |
function tweakDefence(e, def, hpMult = 1.0) { | |
local b = e.m.BaseProperties; | |
b.MeleeDefense += def; | |
b.RangedDefense += def; | |
b.HitpointsMult *= hpMult; | |
} | |
// Presentation utils | |
function scale(e, scaleMult) { | |
// This doesn't take care of the corpse size unfortunately | |
local parts = [ | |
"body", "surcoat", "tattoo_body", "armor", | |
"head", "hair", "beard", "beard_top", "tattoo_head", "helmet" | |
]; | |
// "accessory", "accessory_special" ??? | |
foreach (part in parts) { | |
e.getSprite(part).Scale *= scaleMult; | |
} | |
} | |
function color(e, part, color, brightness = 1, saturation = 1) { | |
local sprite = e.getSprite(part); | |
sprite.Color = this.createColor(color); | |
sprite.setBrightness(brightness); | |
sprite.Saturation = saturation; | |
} | |
} | |
// Utilities | |
gt.StandoutFoes.Rand <- { | |
chance = @(prob) this.Math.rand(1, 1000) / 1000 <= prob, | |
choice = @(arr) arr[this.Math.rand(0, arr.len() - 1)] | |
} | |
// Debug things | |
local PP_MAX_LENGTH = 50; | |
local function indent(level, s) { | |
return format("%"+ (level * 4) + "s", "") + s; | |
} | |
local function join(sep, lines) { | |
local s = ""; | |
foreach (i, line in lines) { | |
if (i > 0) s += sep; | |
s += line; | |
} | |
return s; | |
} | |
local function joinLength(items, sepLen) { | |
if (items.len() == 0) return 0; | |
return items.map(@(s) s.len()).reduce(@(a, b) a + b) + (items.len() - 1) * sepLen; | |
} | |
local function mapTable(data, func) { | |
local res = []; | |
// this.logInfo | |
foreach (key, value in data) res.push(func(key, value)); | |
return res; | |
} | |
Debug.pp <- function(data, level = 0) { | |
local function ppCont(items, level, start, end) { | |
if (joinLength(items, 2) <= PP_MAX_LENGTH - level * 4 - 2) { | |
return start + join(", ", items) + end + (level == 0 ? "\n" : ""); | |
} else { | |
local lines = [start]; | |
lines.extend(items.map(@(item) indent(level + 1, item))); | |
lines.push(indent(level, end)); | |
return join("\n", lines) + "\n"; | |
} | |
} | |
if (typeof data == "table") { | |
local items = mapTable(data, @(k, v) k + " = " + Debug.pp(v, level + 1)); | |
return ppCont(items, level, "{", "}"); | |
} else if (typeof data == "array") { | |
local items = data.map(@(item) Debug.pp(item, level + 1)); | |
return ppCont(items, level, "[", "]"); | |
} else { | |
return "" + data; // More robust than .tostring() | |
} | |
} | |
Debug.log <- function(name, data) { | |
this.logInfo("<pre>sf: " + name + " = " + Debug.pp(data) + "</pre>"); | |
} | |
::mods_queue("mod_standout_foes", null, function() | |
{ | |
this.logInfo("sf: loading"); | |
::mods_hookClass("entity/tactical/tactical_entity_manager", function(cls) { | |
this.logInfo("sf: hook tactical_entity_manager"); | |
local setupEntity = cls.setupEntity; | |
cls.setupEntity = function(e, t) { | |
setupEntity(e, t); | |
// this.logInfo("sf: setupEntity " + e.getName() + " troop " + t + " party " + t.Party.getName()); | |
this.logInfo("sf: setupEntity " + e.getName() + " party " + t.Party.getName()); | |
if (!("sf" in t.Party)) t.Party.m.StandoutFoes <- sf.partyStats(t.Party); | |
Debug.log("troop", t); | |
Debug.log("party.sf", t.Party.m.StandoutFoes); | |
Debug.log("party.m", t.Party.m); | |
// this.logInfo("sf: " + Debug.html("party.sf", t.Party.m.StandoutFoes)); | |
// this.logInfo("sf: troop: " + Debug.html(t)); | |
// this.logInfo("sf: party.m: " + Debug.pp(t.Party.m)); | |
// this.logInfo("sf: end party.m"); | |
// == this.Const.FactionType.Goblins | |
// foreach (key, value in t) { | |
// this.logInfo("sf: troop: " + key + " = " + value); | |
// } | |
// this.logInfo("sf: party.m typeof: " + typeof t.Party.m); | |
// foreach (key, value in t.Party.m) { | |
// this.logInfo("sf: party: " + key); | |
// this.logInfo("sf: party: " + Debug.html(value)); | |
// } | |
// foreach (key, value in t.Party.world_entity.m) { | |
// this.logInfo("sf: party.we: " + key + " = " + value); | |
// } | |
// this.logInfo("sf: party.getFaction(): " + t.Party.getFaction()); | |
// foreach (key, value in t.Party.m.Loot) { | |
// this.logInfo("sf: party.loot: " + key + " = " + value); | |
// } | |
// foreach (key, value in t.Party.world_entity.m.Troops[0]) { | |
// this.logInfo("sf: party.we.troops[0]: " + key + " = " + value); | |
// } | |
// foreach (key, value in t.Party.m.Flags) { | |
// this.logInfo("sf: party.flags: " + key + " = " + value); | |
// } | |
local quirks = [sf.Quirks.fast, sf.Quirks.big]; | |
sf.Util.applyQuirk(e, sf.Rand.choice(quirks)); | |
// e.m.Skills.add(this.new("scripts/skills/racial/champion_racial")); | |
} | |
}); | |
// local patchPlayer = function(obj) { | |
// this.logInfo("MVAOP: patching a player object " + obj.getName()); | |
// // Prevent from patching twice. | |
// // Not sure how this happens, but it does sometimes for new hires, who get levels later. | |
// if("addExtraPerks" in obj) return; | |
// // Give rolls of 2 on veteran levels to attrs with talents sometimes | |
// local getAttributeLevelUpValues = obj.getAttributeLevelUpValues; | |
// obj.getAttributeLevelUpValues = function() | |
// { | |
// if(m.Attributes[0].len() != 0) return getAttributeLevelUpValues(); // no bonus if not in veteran levels | |
// local v = getAttributeLevelUpValues(); | |
// local extra = function(t, bonus = 0) | |
// { | |
// if(t == 0) return 0; | |
// local max = (t == 1 ? 5 : t == 2 ? 3 : 2) - bonus; | |
// return max <= 1 || Math.rand(1, max) == 1 ? 1 : 0; | |
// } | |
// v.hitpointsIncrease += extra(m.Talents[Const.Attributes.Hitpoints]); | |
// v.braveryIncrease += extra(m.Talents[Const.Attributes.Bravery]); | |
// v.fatigueIncrease += extra(m.Talents[Const.Attributes.Fatigue]); | |
// v.initiativeIncrease += extra(m.Talents[Const.Attributes.Initiative], 1); | |
// v.meleeSkillIncrease += extra(m.Talents[Const.Attributes.MeleeSkill]); | |
// v.rangeSkillIncrease += extra(m.Talents[Const.Attributes.RangedSkill]); | |
// v.meleeDefenseIncrease += extra(m.Talents[Const.Attributes.MeleeDefense]); | |
// v.rangeDefenseIncrease += extra(m.Talents[Const.Attributes.RangedDefense]); | |
// return v; | |
// } | |
// // Add an extra perk point for each odd level | |
// local updateLevel = obj.updateLevel; | |
// obj.updateLevel = function() { | |
// // this.logInfo("MVAOP: update level for " + this.getName()); | |
// local level = m.Level; | |
// updateLevel(); | |
// this.addExtraPerks(level); | |
// } | |
// // Perks are added manually there, breaking the abstraction | |
// local onHired = obj.onHired; | |
// obj.onHired = function() { | |
// this.logInfo("MVAOP: hired " + this.getName()); | |
// onHired(); | |
// this.addExtraPerks(1); | |
// } | |
// obj.addExtraPerks <- function(level) { | |
// if (level >= m.Level) return; | |
// this.logInfo("MVAOP: Leveling up " + this.getName() + " from " + level + " to " + m.Level); | |
// // give a perk point every for odd level | |
// for (; ++level <= m.Level;) | |
// if (level % 2 == 1) m.PerkPoints++; | |
// } | |
// } | |
// // Freshly hired bros don't fire new hook and hence don't get a patched class, | |
// // so we need to patch it like this | |
// ::mods_hookNewObject("entity/tactical/player", patchPlayer); | |
// ::mods_hookNewObject("ui/screens/world/modules/world_town_screen/town_hire_dialog_module", | |
// function (cls) { | |
// this.logInfo("MVAOP: patching town_hire_dialog_module class"); | |
// local onHireRosterEntry = cls.onHireRosterEntry; | |
// cls.onHireRosterEntry = function(_entityID) { | |
// local entry = this.findEntityWithinRoster(_entityID); | |
// if(entry) patchPlayer(entry); | |
// return onHireRosterEntry(_entityID) | |
// } | |
// } | |
// ); | |
// // Need to hook this too since perks are added there too, | |
// // breaking updateLevel() abstraction | |
// ::mods_hookBaseClass("scenarios/world/starting_scenario", function(cls) { | |
// this.logInfo("MVAOP: hook starting_scenario"); | |
// local onSpawnAssets = "onSpawnAssets" in cls ? cls.onSpawnAssets : null; | |
// cls.onSpawnAssets <- function() | |
// { | |
// if (onSpawnAssets) onSpawnAssets(); | |
// local roster = World.getPlayerRoster().getAll(); | |
// foreach (bro in roster) bro.addExtraPerks(1); | |
// } | |
// }); | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment