Files
Server/src/engine/net/client/handlers/ObjectActionMsgHandler.java
T
MagicBotandClaude Opus 5.5 2774d8a510 Read offering info and bane rank through the bridge
Shrine boons read getItemOfferingInfo() and the banestone and bane
production paths read getItemBaneRank() on template.cacheTemplate.

The legacy parser only kept item_bane_rank when ACTIONRESPONSE is
4250517122; the cache parsers always read it. Parity shows this makes
no difference: every template outside that gate has bane rank 0.

Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
item_offering_info and item_bane_rank. No value changes expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:46:14 -04:00

288 lines
12 KiB
Java

package engine.net.client.handlers;
import engine.gameManager.*;
import engine.math.Bounds;
import engine.math.Vector3fImmutable;
import engine.mbEnums;
import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.objects.*;
import engine.parsers.objects.DeedTemplate;
import engine.powers.PowersBase;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
* @Author:
* @Summary: Processes application protocol message which handles
* items in the character's inventory which are used. Potions, Deeds, etc.
*/
public class ObjectActionMsgHandler extends AbstractClientMsgHandler {
// Reentrant lock for dropping banes
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public ObjectActionMsgHandler() {
super();
}
@Override
protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) {
// Member variable declaration
ObjectActionMsg msg;
PlayerCharacter player;
CharacterItemManager itemMan;
ArrayList<Long> comps;
Dispatch dispatch;
boolean waterbucketBypass = false;
// Member variable assignment
msg = (ObjectActionMsg) baseMsg;
player = SessionManager.getPlayerCharacter(origin);
if (player == null) {
return true;
}
itemMan = player.charItemManager;
if (itemMan == null) {
return true;
}
comps = msg.getTargetCompID();
if (comps.isEmpty()) {
return true;
}
long comp = comps.get(0);
if (((int) comp) != 0) {
Item item = Item.getFromCache((int) comp);
if (item == null) {
return true;
}
//dupe check
if (!item.validForInventory(origin, player, itemMan)) {
return true;
}
if (itemMan.doesCharOwnThisItem(item.getObjectUUID())) {
int uuid = item.templateID;
switch (item.template.cacheTemplate.getItemType()) {
case DEED: //buildings
//Call add building screen here, ib.getUseID() get's building ID
//if inside player city, center loc on tol. otherwise center on player.
Vector3fImmutable loc = player.getLoc();
Zone zone = ZoneManager.findSmallestZone(player.getLoc());
if (zone != null) {
if (zone.guild_zone) {
loc = zone.getLoc();
}
}
PlaceAssetMsg pam = new PlaceAssetMsg();
pam.setActionType(2);
pam.setContractID(item.getObjectUUID());
pam.setX(loc.getX() + 64); //offset grid from tol
pam.setY(loc.getY());
pam.setZ(loc.getZ() + 64); //offset grid from tol
pam.addPlacementInfo(DeedTemplate._deeds.get(item.templateID).deed_structure_id);
dispatch = Dispatch.borrow(player, pam);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
//itemMan.consume(item); //temporary fix for dupe.. TODO Make Item Unusable after This message is sent.
break;
case FURNITUREDEED: //furniture
//Call add furniture screen here. ib.getUseID() get's furniture ID
break;
case OFFERING:
long shrineCompID = comps.get(1);
Building shrineBuilding = BuildingManager.getBuilding((int) shrineCompID);
if (shrineBuilding == null) {
return true;
}
if (shrineBuilding.getBlueprint() != null && shrineBuilding.getBlueprint().getBuildingGroup() != mbEnums.BuildingGroup.SHRINE) {
return true;
}
if (shrineBuilding.getRank() == -1) {
return true;
}
Shrine shrine = Shrine.shrinesByBuildingUUID.get(shrineBuilding.getObjectUUID());
if (shrine == null) {
return true;
}
if (shrine.addFavor(player, item)) {
shrineBuilding.addEffectBit(1000000 << 2);
shrineBuilding.updateEffects();
shrineBuilding.removeEffectBit(1000000 << 2);
}
break;
case REALMCHARTER:
int charterType = 0;
switch (uuid) {
case 910020:
charterType = 762228431;
break;
case 910021:
charterType = -15978914;
break;
case 910022:
charterType = -600065291;
break;
}
if (Realm.claimRealm(player, charterType) == true) {
itemMan.consume(item);
}
break;
case WAND: //rod of command
long compID = comps.get(1);
int objectType = AbstractWorldObject.extractTypeID(compID).ordinal();
Mob toCommand;
if (objectType == mbEnums.GameObjectType.Mob.ordinal()) {
toCommand = Mob.getFromCache((int) compID);
} //Only Command Mob Types.
else {
return true;
}
if (toCommand == null) {
return true;
}
if (!toCommand.isSiege())
return true;
if (player.commandSiegeMinion(toCommand)) {
itemMan.consume(item);
}
break;
//ANNIVERSERY GIFT
case TREASURE:
// *** Disabled for now: Needs bootyset created
LootManager.peddleFate(player, item);
break;
case BUCKET: //water bucket
case POTION: //potions, tears of saedron
case SCROLL: //runes, petition, warrant, scrolls
if (uuid > 680069 && uuid < 680074) //Handle Charter, Deed, Petition, Warrant here
break;
if (item.template.cacheTemplate.getItemBaneRank() > 0) {
// Only one banestone at a time
lock.writeLock().lock();
try {
if (Bane.summonBanestone(player, origin, item.template.cacheTemplate.getItemBaneRank()) == true)
itemMan.consume(item);
} finally {
lock.writeLock().unlock();
}
break;
}
if (uuid == 910010) { //tears of saedron
if (comps.size() > 1)
AbstractCharacter.removeRune(player, origin, comps.get(1).intValue());
break;
}
if (item.chargesRemaining > 0) {
ArrayList<Long> tarList = msg.getTargetCompID();
AbstractWorldObject target = player;
if (tarList.size() > 1) {
long tarID = tarList.get(1);
if (tarID != 0) {
AbstractGameObject tarAgo = AbstractGameObject.getFromTypeAndID(tarID);
if (tarAgo instanceof AbstractWorldObject) {
target = (AbstractWorldObject) tarAgo;
}
}
}
// Bypass for waterbuckets
if (item.template.cacheTemplate.getItemType().equals(mbEnums.ItemType.BUCKET)) {
// test for valid target type
if (target.getObjectType() == mbEnums.GameObjectType.PlayerCharacter)
waterbucketBypass = true;
else {
// test distance to structure
Building targetBuilding = (Building) target;
Bounds testBounds = Bounds.borrow();
testBounds.setBounds(player.getLoc(), 25);
if (Bounds.collide(targetBuilding.getBounds(), testBounds, .1f) == false) {
ChatManager.chatSystemError(player, "Not in range of structura for to heal!");
return true;
}
}
// Send piss bucket animation
VisualUpdateMessage vum = new VisualUpdateMessage(player, 16323);
vum.configure();
DispatchManager.sendToAllInRange(player, vum);
}
if (waterbucketBypass == false) {
String powerString = item.template.item_power_grant.keySet().iterator().next();
PowersBase powerAction = PowersManager.powersBaseByIDString.get(powerString);
int powerValue = item.template.item_power_grant.get(powerString);
PowersManager.applyPower(player, target, Vector3fImmutable.ZERO, powerAction.getToken(), powerValue, true);
}
itemMan.consume(item);
} else //just remove the item at this point
itemMan.consume(item);
dispatch = Dispatch.borrow(player, msg);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
player.cancelOnSpell();
break;
case RUNE:
if(ApplyRuneMsg.applyRune(uuid, origin, player)) {
itemMan.consume(item);
}
break;
default: //shouldn't be here, consume item
dispatch = Dispatch.borrow(player, msg);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
// itemMan.consume(item);
}
} else {
// TODO log item does not belong to player
// System.out.println("Item does not belong to player");
// Cleanup duped item here
}
}
return true;
}
}