Files
magicbane/src/engine/powers/poweractions/StealPowerAction.java
T
MagicBotandClaude Opus 5.5 3fc5e7253a Build power actions from PowerActions.cfg
PowersManager.loadPowerActions builds every AbstractPowerAction from
PowerActionTemplate (template ctor on all subclasses), in id order with IDs
1..3046; OPENGATE keeps 5000. The ResultSet loader stays until removal.
The parser now reads the SimpleDamage (min, max), Teleport (bool), Track
(two bools) and TransferStat (bool at token 8) header fields.

Parity (JSON-Refactor-Plan/scripts/p47.py, every column): ids 3046/3046 plus
the DB-only AELFBORN row, which nothing references (the cfg has
BOON-AELFBORN). Effect ids, deferred powers, RemoveEffect source/REMOVEALL,
track filter/max, transfer types/ramps/ramp-add, mob level identical. The old
flags match the new header fields: Track 1024/2048 = tokens 3/4,
TransferStat 16384 = token 8, Teleport 32768 = SAB-001A's TRUE.

Value changes (PowerActions.cfg wins):
- DamageOverTime/TransferStatOT: the cfg value is the tick count; tick length
  = action duration / ticks. The DB stored cfg+1 as tick seconds, which only
  worked for 30s DoTs (5 x 6s, unchanged). HOLY-DOT-17 (60s) goes 10 -> 5
  ticks, BLEED-DOT (4s) 0 -> 4, TransferStatOT 30s 6 -> 5 ticks.
- TransferStat amounts are read with ToInt as the client does: 16 actions
  drop by 0.5 (BEL-001A 31.5 -> 31, CON-001A 13.5 -> 13, ...).
- PRE-334..337 become ApplyEffects with one effect; ApplyEffectsPowerAction
  now allows a missing second effect on baked-in items.
- Charm level cap ramp from the LEVELCAP= curve (same values as the DB).
- Charm actions with effect FEAR/CONFUSION (12, DB type Confusion) stay
  ConfusionPowerAction no-ops; CHARM ones use CharmPowerAction (decided).
- CreateMob: the cfg client mob id (49..74, 3500, 3501) maps to the mobbase
  through a map in CreateMobPowerAction, from the old DB values.
- The 50 Charm/Block/SpireDisable rows with no DB effect id get the cfg id
  (unread by those classes); isAggressive differs on 330 rows (unread).
- SimpleDamage keeps min/max instead of the always-0 simpleDamage (unread).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:37:36 -04:00

208 lines
7.6 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.powers.poweractions;
import engine.gameManager.ChatManager;
import engine.gameManager.CombatManager;
import engine.gameManager.DispatchManager;
import engine.math.Vector3fImmutable;
import engine.mbEnums;
import engine.mbEnums.ItemType;
import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.LootMsg;
import engine.objects.*;
import engine.parsers.objects.wpak.PowerActionTemplate;
import engine.powers.ActionsBase;
import engine.powers.PowersBase;
import engine.server.MBServerStatics;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.ThreadLocalRandom;
import static engine.math.FastMath.sqr;
public class StealPowerAction extends AbstractPowerAction {
/**
* ResultSet Constructor
*/
public StealPowerAction(ResultSet rs) throws SQLException {
super(rs);
}
public StealPowerAction(PowerActionTemplate powerActionTemplate, int id) {
super(powerActionTemplate, id);
}
protected static boolean stealSuccess(PlayerCharacter pc, AbstractWorldObject awo) {
if (pc == null || awo == null || !AbstractWorldObject.IsAbstractCharacter(awo) || pc.getPowers() == null)
return false;
int levelDif = pc.getLevel() - ((AbstractCharacter) awo).getLevel();
if (!pc.getPowers().containsKey(429396028))
return false;
CharacterPower cp = pc.getPowers().get(429396028);
int trains = cp.getTotalTrains();
float chance = 20 + (trains * 1.5f) + levelDif;
chance = (chance < 5f) ? 5f : chance;
chance = (chance > 85f) ? 85f : chance;
float roll = ThreadLocalRandom.current().nextFloat() * 100f;
return roll < chance;
}
//called to get amount of gold to steal between 0 and max gold
protected static int getAmountToSteal(Item item) {
if (item.template.getItemType().equals(ItemType.GOLD)) {
int amount = item.getNumOfItems();
if (amount < 1)
return -1;
int a = ThreadLocalRandom.current().nextInt(amount + 1);
int b = ThreadLocalRandom.current().nextInt(amount + 1);
int c = ThreadLocalRandom.current().nextInt(amount + 1);
return (a + b + c) / 3;
} else
return 0;
}
@Override
protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) {
if (source == null || awo == null || !(source.getObjectType().equals(mbEnums.GameObjectType.PlayerCharacter)) || !(awo.getObjectType().equals(mbEnums.GameObjectType.Item)))
return;
PlayerCharacter sourcePlayer = (PlayerCharacter) source;
if (sourcePlayer.isSafeMode())
return;
if (!sourcePlayer.isAlive())
return;
//prevent stealing no steal mob loot
if (awo instanceof MobLoot && ((MobLoot) awo).noSteal())
return;
Item tar = (Item) awo;
AbstractWorldObject owner = (AbstractWorldObject) tar.getOwner();
if (owner == null)
return;
AbstractCharacter ownerAC = null;
if (AbstractWorldObject.IsAbstractCharacter(owner))
ownerAC = (AbstractCharacter) owner;
if (ownerAC != null)
if (ownerAC.getLoc().distanceSquared(sourcePlayer.getLoc()) > sqr(MBServerStatics.LOOT_RANGE))
return;
//only steal from players or mobs
if (owner.getObjectType().equals(mbEnums.GameObjectType.PlayerCharacter)) {
PlayerCharacter ownerPC = (PlayerCharacter) owner;
if (ownerPC.isSafeMode() || sourcePlayer.inSafeZone() || ownerPC.inSafeZone())
return;
if (ownerPC.getLoc().distanceSquared(sourcePlayer.getLoc()) > sqr(MBServerStatics.LOOT_RANGE))
return;
//dupe check, validate player has item
if (!tar.validForInventory(ownerPC.getClientConnection(), ownerPC, ownerPC.charItemManager))//pc.getCharItemManager()))
return;
//mark thief and target as player aggressive
sourcePlayer.setLastPlayerAttackTime();
ownerPC.setLastPlayerAttackTime();
//Handle target attacking back if in combat and has no other target
CombatManager.handleRetaliate(ownerAC, sourcePlayer);
} else
return;
ClientConnection origin = sourcePlayer.getClientConnection();
if (origin == null)
return;
int amount = getAmountToSteal(tar);
//test probability of steal success
if (!stealSuccess(sourcePlayer, owner)) {
ChatManager.chatPeekSteal(sourcePlayer, ownerAC, tar, false, false, -1);
return;
} else {
ChatManager.chatPeekSteal(sourcePlayer, ownerAC, tar, true, false, amount);
//TODO send steal failure success
}
//attempt transfer item
CharacterItemManager myCIM = sourcePlayer.charItemManager;
CharacterItemManager ownerCIM = ((AbstractCharacter) owner).charItemManager;
if (myCIM == null || ownerCIM == null)
return;
if (tar.template.getItemType().equals(ItemType.GOLD)) {
//stealing gold
if (!myCIM.transferGoldToMyInventory((AbstractCharacter) owner, amount))
return;
} else {
//stealing items
if (ownerCIM.lootItemFromMe(tar, sourcePlayer, origin, true, amount) == null)
return;
}
//send loot message to person stealing.
LootMsg lm = new LootMsg(source.getObjectType().ordinal(), source.getObjectUUID(), owner.getObjectType().ordinal(), owner.getObjectUUID(), tar);
Dispatch dispatch = Dispatch.borrow(sourcePlayer, lm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
//update thief's inventory
if (sourcePlayer.charItemManager != null)
sourcePlayer.charItemManager.updateInventory();
//update victims inventory
if (owner.getObjectType().equals(mbEnums.GameObjectType.PlayerCharacter)) {
PlayerCharacter ownerPC = (PlayerCharacter) owner;
if (ownerPC.charItemManager != null)
ownerPC.charItemManager.updateInventory();
}
//TODO if victim is trading, cancel trade window for both people involved in trade
}
@Override
protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) {
}
@Override
protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc,
int numTrains, ActionsBase ab, PowersBase pb, int duration) {
// TODO Auto-generated method stub
}
}