Skip to content

Instantly share code, notes, and snippets.

@Bind
Created February 20, 2022 02:11
Show Gist options
  • Save Bind/269457dfdfbd7764b4b9424a18f30b2f to your computer and use it in GitHub Desktop.
Save Bind/269457dfdfbd7764b4b9424a18f30b2f to your computer and use it in GitHub Desktop.
df repeat attack plugin
//@ts-ignore
import { isUnconfirmedMoveTx } from "https://cdn.skypack.dev/@darkforest_eth/serde";
import {
html,
render,
useEffect,
useState,
useLayoutEffect,
//@ts-ignore
} from "https://unpkg.com/htm/preact/standalone.module.js";
function unconfirmedDepartures(planet) {
return (
planet.transactions
?.getTransactions(isUnconfirmedMoveTx)
//@ts-ignore
.reduce((acc, tx) => acc + tx.intent.forces, 0) || 0
);
}
function planetCurrentPercentEnergy(planet) {
const departures = unconfirmedDepartures(planet);
const estimatedEnergy = Math.floor(planet.energy - departures);
return Math.floor((estimatedEnergy / planet.energyCap) * 100);
}
// I use `let` here to sidestep any weird execution env problems
let PERCENTAGE_TRIGGER = 75;
let PERCENTAGE_SEND = 45;
class Repeater {
attacks = [];
intervalId;
constructor() {
if (typeof window.__CORELOOP__ == "undefined") {
//setup append only interval id storage
window.__CORELOOP__ = [];
} else {
//clear out old intervald
console.log("KILLING PREVIOUS INTERVALS");
window.__CORELOOP__.forEach((id) => window.clearInterval(id));
}
this.attacks = [];
this.intervalId = window.setInterval(this.coreLoop.bind(this), 15000);
window.__CORELOOP__.push(this.intervalId);
}
addAttack(srcId, targetId) {
this.attacks.push({ srcId, targetId });
}
removeAttack(position) {
this.attacks.splice(position, 1);
}
coreLoop() {
this?.attacks?.forEach((a) => {
ExecuteAttack(a.srcId, a.targetId);
});
}
}
const ExecuteAttack = (srcId, targetId) => {
let srcPlanet = df.getPlanetWithId(srcId);
if (!srcPlanet) {
// Well shit
return;
}
// Needs updated check getUnconfirmedDepartingForces
const departingForces = unconfirmedDepartures(srcPlanet);
const TRIGGER_AMOUNT = Math.floor(
(srcPlanet.energyCap * PERCENTAGE_TRIGGER) / 100
);
const FUZZY_ENERGY = Math.floor(srcPlanet.energy - departingForces); //Best estimate of how much energy is ready to send
if (FUZZY_ENERGY > TRIGGER_AMOUNT) {
const overflow_send =
planetCurrentPercentEnergy(srcPlanet) -
(PERCENTAGE_TRIGGER - PERCENTAGE_SEND);
const FORCES = Math.floor((srcPlanet.energyCap * overflow_send) / 100);
df.move(srcId, targetId, FORCES, 0);
}
};
let Spacing = {
marginLeft: "12px",
marginRight: "12px",
};
let VerticalSpacing = {
marginBottom: "12px",
};
let HalfVerticalSpacing = {
marginBottom: "6px",
};
let Clickable = {
cursor: "pointer",
textDecoration: "underline",
};
let ActionEntry = {
marginBottom: "5px",
display: "flex",
justifyContent: "space-between",
color: "",
};
function centerPlanet(id) {
ui.centerLocationId(id);
}
function planetShort(locationId) {
return locationId.substring(4, 9);
}
function Attack({ attack, onDelete }) {
return html`
<div style=${ActionEntry}>
<span>
<span
style=${{ ...Spacing, ...Clickable }}
onClick=${() => centerPlanet(attack.srcId)}
>${planetShort(attack.srcId)}</span
>
=>
<span
style=${{ ...Spacing, ...Clickable }}
onClick=${() => centerPlanet(attack.targetId)}
>${planetShort(attack.targetId)}</span
></span
>
<button onClick=${onDelete}>X</button>
</div>
`;
}
function AddAttack({ onCreate }) {
let [planet, setPlanet] = useState(ui.getSelectedPlanet() || undefined);
let [source, setSource] = useState(undefined);
let [target, setTarget] = useState(undefined);
useLayoutEffect(() => {
let onClick = () => {
setPlanet(ui.getSelectedPlanet());
};
window.addEventListener("click", onClick);
return () => {
window.removeEventListener("click", onClick);
};
}, []);
return html`
<div style=${{ display: "flex" }}>
<button style=${VerticalSpacing} onClick=${() => setSource(planet)}>
Set Source
</button>
<span style=${{ ...Spacing, marginRight: "auto" }}
>${source ? planetShort(source.locationId) : "?????"}</span
>
<button style=${VerticalSpacing} onClick=${() => setTarget(planet)}>
Set Target
</button>
<span style=${{ ...Spacing, marginRight: "auto" }}
>${target ? planetShort(target.locationId) : "?????"}</span
>
<button
style=${VerticalSpacing}
onClick=${() =>
target && source && onCreate(source.locationId, target.locationId)}
>
start
</button>
</div>
`;
}
function AttackList({ repeater }) {
const [attacks, setAttacks] = useState([...repeater.attacks]);
useEffect(() => {
const id = setInterval(() => {
setAttacks([...repeater.attacks]);
}, 1000);
setAttacks([...repeater.attacks]);
return () => clearInterval(id);
}, [attacks.length]);
let actionList = {
maxHeight: "70px",
overflowX: "hidden",
overflowY: "scroll",
};
//@ts-ignore
let actionsChildren = attacks.map((action, index) => {
return html`
<${Attack}
attack=${action}
onDelete=${() => {
repeater.removeAttack(index);
}}
/>
`;
});
return html`
<h1>Set-up a Recurring Attack</h1>
<i style=${{ ...VerticalSpacing, display: "block" }}
>Auto-attack when source planet >75% energy
</i>
<${AddAttack}
onCreate=${(source, target) => repeater.addAttack(source, target)}
/>
<h1 style=${HalfVerticalSpacing}>
Recurring Attacks (${actionsChildren.length})
<button
style=${{ float: "right" }}
onClick=${() => setAttacks([...repeater.attacks])}
>
refresh
</button>
</h1>
<div style=${actionList}>
${actionsChildren.length ? actionsChildren : "No Actions."}
</div>
`;
}
function App({ repeater }) {
return html`<${AttackList} repeater=${repeater} />`;
}
class Plugin {
repeater;
container;
root;
stop() {
window.__CORELOOP__.forEach((id) => window.clearInterval(id));
}
constructor() {
this.repeater = new Repeater();
this.root = undefined;
}
/**
* Called when plugin is launched with the "run" button.
*/
async render(container) {
this.container = container;
container.style.width = "380px";
this.root = render(html`<${App} repeater=${this.repeater} />`, container);
}
/**
* Called when plugin modal is closed.
*/
destroy() {
//@ts-ignore
window.__CORELOOP__.forEach((id) => window.clearInterval(id));
if (this.container) render(html`<div></div>`, this.container);
}
}
/**
* And don't forget to export it!
*/
export default Plugin;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment