forked from MagicBane/Server
Deed placement (ObjectActionMsgHandler, PlaceAssetMsgHandler) reads deed_structure_id from DeedTemplate._deeds. The deed, employment contract and charter type checks (PlaceAssetMsgHandler, ActivateNPCMsgHandler, GuildCreationFinalizeHandler, UseCharterMsg, Item.serialize) read item_type through the cache template bridge. Charters go through the shared getter rather than _deeds because template 545 "Charter" is a CHARTER in the ITEM folder. Parity (offline, static_item_templates vs COBJECTS, scripts/p13.py, 881 templates: all 880 DEED plus ITEM 545): 0 differences in item_type and deed_structure_id. Every DB DEED, EMPLOYMENTCONTRACT, PETTOTEM and FURNITUREDEED id is in the DEED folder. No value changes expected. No pet totem or furniture deed consumer reads template fields, so neither changes. JSON-Refactor-Plan Task 1.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
288 lines
12 KiB
Java
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.item_type) {
|
|
|
|
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.item_bane_rank > 0) {
|
|
|
|
// Only one banestone at a time
|
|
|
|
lock.writeLock().lock();
|
|
|
|
try {
|
|
if (Bane.summonBanestone(player, origin, item.template.item_bane_rank) == 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.item_type.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;
|
|
}
|
|
|
|
}
|