Rewrote impling system

Fixed static impling spawns
Fixed impling spawn ratio
Reworked Puro-Puro, much more authentic
Imp defenders now work properly
Crop circles now appear
Added admin command ::implings to list active implings (excludes implings from Puro-Puro)
Fixed bug where you could add to a finished compost bin
This commit is contained in:
Ceikry
2023-04-20 12:16:21 +00:00
committed by Ryan
parent 41c970efd4
commit f6096e3020
24 changed files with 915 additions and 457 deletions
@@ -0,0 +1,97 @@
package content.minigame.puropuro
import core.api.*
import core.game.interaction.*
import core.game.world.map.Location
import core.game.node.scenery.Scenery
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.TeleportManager.TeleportType
class CropCircleController : TickListener, InteractionListener, LoginListener {
override fun tick () {
if (getWorldTicks() < nextCircle)
return
deconstructOldCircle()
val (name, loc) = possibleLocations.random()
constructCircle(loc)
sendNews("A crop circle has appeared near $name.")
nextCircle = getWorldTicks() + 1500
currentLocName = name
}
override fun defineListeners() {
on (center, IntType.SCENERY, "enter") {player, _ ->
if (hasImpBox(player)) {
sendDialogue (player, "Something prevents you from entering. You think the portal is offended by your imp box. They are not popular on imp and impling planes.")
return@on true
}
teleport (player, puroLocation, TeleportType.PURO_PURO)
setAttribute (player, exitLocation, Location.create(player.location))
return@on true
}
on (puroExit, IntType.SCENERY, "leave", "quick-leave") {player, _ ->
var exit = getAttribute (player, exitLocation, Location.create(3158, 3300, 0))
teleport (player, exit, TeleportType.PURO_PURO)
return@on true
}
}
override fun login (player: Player) {
sendMessage (player, "A crop circle is active near $currentLocName.")
}
private fun constructCircle (location: Location) {
activeObjects.add (
addScenery (
center,
location,
rotation = 0,
type = 10
)
)
for ((index, tile) in location.surroundingTiles.withIndex()) {
activeObjects.add (
addScenery (
surrounding[index % 4],
tile,
rotation = (index / 4) * 2,
type = 10
)
)
}
}
private fun deconstructOldCircle() {
for (scenery in activeObjects)
removeScenery(scenery)
activeObjects.clear()
}
private fun hasImpBox (player: Player) : Boolean {
return inInventory(player, 10025) || inInventory(player, 10027) || inInventory(player, 10028)
}
companion object {
var currentLocName = ""
val exitLocation = "/save:puro-exit"
val possibleLocations = arrayOf (
Pair ("Doric's Hut", Location.create(2953, 3444, 0)),
Pair ("Yanille", Location.create(2583, 3104, 0)),
Pair ("Draynor", Location.create(3113, 3274, 0)),
Pair ("Rimmington", Location.create(2978, 3216, 0)),
Pair ("The Grand Exchange", Location.create(3141, 3461, 0)),
Pair ("Northern Lumbridge", Location.create(3160, 3296, 0)),
Pair ("Southern Varrock", Location.create(3218, 3348, 0)),
Pair ("Northern Ardougne", Location.create(2644, 3350, 0))
)
val activeObjects = ArrayList<Scenery>()
val surrounding = arrayOf (24984, 24985, 24986, 24987)
val center = 24991
val puroExit = 25014
val puroLocation = Location.create(2591, 4320, 0)
var nextCircle = 0
}
}
@@ -0,0 +1,93 @@
package content.minigame.puropuro
import core.api.*
import core.tools.*
import core.game.node.entity.npc.*
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.GroundItemManager
import core.game.node.item.Item
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.RegionManager
import core.game.system.task.Pulse
import content.global.skill.hunter.bnet.BNetTypes
import org.rs09.consts.Items
class ImpDefenderBehavior : NPCBehavior(6074) {
override fun onCreation (self: NPC) {
self.isWalks = true
self.isNeverWalks = false
self.walkRadius = 30
}
override fun tick (self: NPC) : Boolean {
if (!RandomFunction.roll(10)) return true
var nextCaptureTick = getAttribute(self, "next-capture-tick", 0)
if (getWorldTicks() < nextCaptureTick) return true
var players = RegionManager.getLocalPlayers(self, 2)
for (player in players) {
var lowestTierImpling = BNetTypes.getImpling(player)
if (lowestTierImpling == null) continue
var jarItem = lowestTierImpling.reward
setAttribute(self, "capture-target", player)
setAttribute(self, "capture-item", jarItem)
setAttribute(self, "next-capture-tick", getWorldTicks() + RandomFunction.random(25, 100))
submitIndividualPulse(self, TryReleasePulse(self))
break
}
return true
}
private class TryReleasePulse(val self: NPC) : Pulse() {
companion object {
const val catchPlayerLow = 35.0 //14% chance to avoid catch at level 1
const val catchPlayerHigh = 280.0 //100% chance to avoid catch at level 90 (level 82 with imp repellent)
const val impRepellentBonus = 20.0 //10% bonus if player has imp repellent
}
var counter = 0
override fun pulse() : Boolean {
val player: Player? = getAttribute(self, "capture-target", null)
val jarItem: Item? = getAttribute(self, "capture-item", null)
if (player == null || jarItem == null) return true
when (counter++) {
0 -> {
face (self, player)
animate (self, 6628)
setDelay(animationDuration(getAnimation(6628)))
return false
}
1 -> {
var hasRepellent = inInventory(player, Items.IMP_REPELLENT_11262)
var baseRoll = RandomFunction.randomDouble(100.0)
var playerRoll = RandomFunction.getSkillSuccessChance(
catchPlayerLow + if (hasRepellent) impRepellentBonus else 0.0,
catchPlayerHigh + if (hasRepellent) impRepellentBonus else 0.0,
getStatLevel(player, Skills.THIEVING)
)
if (playerRoll < baseRoll) {
sendChat (self, "Be free!")
animate (self, 6629)
removeItem (player, jarItem)
var loc = ZoneBorders (
self.location.x - 2,
self.location.y - 2,
self.location.x + 2,
self.location.y + 2
).randomLoc
GroundItemManager.create(
Item(Items.IMPLING_JAR_11260),
loc, player
)
}
resetFace (self)
}
}
return true
}
}
}
@@ -1,112 +0,0 @@
package content.minigame.puropuro;
import java.util.List;
import java.util.Random;
import core.game.node.entity.skill.Skills;
import content.global.skill.hunter.bnet.BNetTypes;
import content.global.skill.hunter.bnet.ImplingNode;
import core.game.node.entity.npc.AbstractNPC;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.game.world.GameWorld;
import core.game.world.map.Location;
import core.game.world.map.RegionManager;
import core.tools.RandomFunction;
/**
* Handles the imp defender npc.
* @author Vexia
*/
public final class ImpDefenderNPC extends AbstractNPC {
/**
* Constructs a new {@code ImpDefenderNPC} {@code Object}.
*/
public ImpDefenderNPC() {
super(0, null);
}
/**
* Constructs a new {@code ImpDefenderNPC} {@code Object}.
* @param id the id.
* @param location the location.
*/
public ImpDefenderNPC(int id, Location location) {
super(id, location);
}
@Override
public void handleTickActions() {
super.handleTickActions();
if (RandomFunction.random(250) <= 3) {
stealImp();
}
}
/**
* Attempts to steal an imp.
*/
private void stealImp() {
List<Player> players = RegionManager.getLocalPlayers(this, 3);
for (Player player : players) {
if (canSteal(player)) {
steal(player);
}
break;
}
}
/**
* Steals an imp from a player.
* @param player the player.
*/
private void steal(Player player) {
if (player == null) {
return;
}
ImplingNode node = BNetTypes.getImpling(player);
if (node != null) {
faceTemporary(player, 1);
sendChat("Be free!");
player.getInventory().remove(node.getReward());
player.setAttribute("imp-steal", GameWorld.getTicks() + 500);
}
}
/**
* Checks if the imp can steal from the player.
* @param player the player.
* @return {@code True} if so.
*/
private boolean canSteal(Player player) {
if (!GameWorld.getSettings().isDevMode() && player.getAttribute("imp-steal", 0) > GameWorld.getTicks()) {
return false;
}
int thievingLevel = player.getSkills().getLevel(Skills.THIEVING);
if (thievingLevel >= 60) {
return false;
}
int level = thievingLevel - 10;
if (level <= 0) {
level = 5;
}
if (player.getInventory().containsItem(new Item(11262))) {
thievingLevel += 15;
}
int currentLevel = RandomFunction.random(thievingLevel) + 1;
double ratio = (double) currentLevel / (new Random().nextInt(level + 5) + 1);
return Math.round(ratio * thievingLevel) < level;
}
@Override
public AbstractNPC construct(int id, Location location, Object... objects) {
return new ImpDefenderNPC(id, location);
}
@Override
public int[] getIds() {
return new int[] { 6074 };
}
}
@@ -35,6 +35,10 @@ import core.plugin.Plugin;
import core.plugin.ClassScanner;
import core.tools.RandomFunction;
import static core.api.ContentAPIKt.animateScenery;
import static core.api.ContentAPIKt.submitWorldPulse;
import static core.api.ContentAPIKt.animationDuration;
/**
* Handles the puro puro activity.
* @author Vexia
@@ -78,7 +82,6 @@ public final class PuroPuroPlugin extends MapZone implements Plugin<Object> {
ClassScanner.definePlugin(new WanderingImplingDialogue());
ClassScanner.definePlugin(new ElnockInquisitorDialogue());
ClassScanner.definePlugin(new PuroOptionHandler());
ClassScanner.definePlugin(new ImpDefenderNPC());
return this;
}
@@ -128,10 +131,7 @@ public final class PuroPuroPlugin extends MapZone implements Plugin<Object> {
case 25021:
pushThrough(p, (Scenery) target);
return true;
case 25014:
p.getTeleporter().send(Location.create(2427, 4446, 0), TeleportType.PURO_PURO);
return true;
}
}
}
return super.interact(e, target, option);
}
@@ -213,10 +213,6 @@ public final class PuroPuroPlugin extends MapZone implements Plugin<Object> {
WHEAT.add(new WheatSet(1, Location.create(2601, 4293, 0), Location.create(2600, 4293, 0)));
WHEAT.add(new WheatSet(0, Location.create(2565, 4310, 0), Location.create(2565, 4311, 0)));
WHEAT.add(new WheatSet(1, Location.create(2582, 4346, 0), Location.create(2583, 4346, 0)));
WHEAT.add(new WheatSet(0, Location.create(2621, 4328, 0), Location.create(2621, 4329, 0)));
WHEAT.add(new WheatSet(1, Location.create(2602, 4349, 0), Location.create(2603, 4349, 0)));
WHEAT.add(new WheatSet(0, Location.create(2562, 4334, 0), Location.create(2562, 4333, 0)));
WHEAT.add(new WheatSet(1, Location.create(2582, 4290, 0), Location.create(2581, 4290, 0)));
WHEAT.add(new WheatSet(0, Location.create(2568, 4348, 0), Location.create(2568, 4347, 0)));
WHEAT.add(new WheatSet(0, Location.create(2615, 4347, 0), Location.create(2615, 4348, 0)));
WHEAT.add(new WheatSet(0, Location.create(2612, 4345, 0), Location.create(2612, 4344, 0)));
@@ -241,7 +237,6 @@ public final class PuroPuroPlugin extends MapZone implements Plugin<Object> {
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
NPCDefinition.forId(6070).getHandlers().put("option:trade", this);
SceneryDefinition.forId(24991).getHandlers().put("option:enter", this);
ItemDefinition.forId(11273).getHandlers().put("option:toggle-view", this);
ItemDefinition.forId(11258).getHandlers().put("option:butterfly-jar", this);
ItemDefinition.forId(11258).getHandlers().put("option:impling-jar", this);
@@ -258,14 +253,6 @@ public final class PuroPuroPlugin extends MapZone implements Plugin<Object> {
case 6070:
ElnockInquisitorDialogue.openShop(player);
break;
case 24991:
if (hasImplingBox(player)) {
player.getDialogueInterpreter().sendDialogue("Something prevents you from entering. You think the portal is offended by", "your imp boxes. They are not popular on imp and impling planes.");
break;
}
player.sendMessage("Puro Puro is disabled until its rewrite/bugfix update is complete.");
//player.getTeleporter().send(Location.create(2591, 4320, 0), TeleportType.PURO_PURO);
return true;
case 11273:
if (!player.getZoneMonitor().isInZone("puro puro")) {
player.sendMessage("You can only use this in the Puro Puro Maze.");
@@ -367,7 +354,6 @@ public final class PuroPuroPlugin extends MapZone implements Plugin<Object> {
/**
* A wheat set.
* @author Vexia
*/
public static class WheatSet {
@@ -434,10 +420,36 @@ public final class PuroPuroPlugin extends MapZone implements Plugin<Object> {
continue;
}
if (removed) {
submitWorldPulse(new Pulse() {
int counter = 0;
@Override
public boolean pulse() {
if (counter++ == 0) {
animateScenery (object, 6596);
setDelay(animationDuration(new Animation(6596)));
return false;
}
return true;
}
});
SceneryBuilder.add(object);
continue;
}
SceneryBuilder.remove(object);
submitWorldPulse(new Pulse() {
int counter = 0;
@Override
public boolean pulse() {
if (counter++ == 0) {
animateScenery (object, 6599);
setDelay(animationDuration(new Animation(6599)));
return false;
}
SceneryBuilder.remove(object);
return true;
}
});
}
removed = !removed;
setNextWhilt();