release-1.3 #13
@@ -20,70 +20,11 @@ The Magicbane Team has wanted to open source Shadowbane for half a decade. We a
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
- Project is released under an MIT license. Please see the LICENSE file.
|
||||||
- Inbound == Outbound.
|
- Inbound == Outbound.
|
||||||
- IntelliJ is the supported IDE.
|
- IntelliJ is the supported IDE.
|
||||||
- Write code today see it running on the Magicbane production server tomorrow.
|
- Write code today see it running on the Magicbane production server tomorrow.
|
||||||
|
|
||||||
## Project setup
|
## Support
|
||||||
|
|
||||||
**Prerequisites**:
|
Documentation is available through the Magicbane [Wiki](http://repo.magicbane.com/MagicBane/Server/wiki) and [Discord server](www.magicbane.com).
|
||||||
|
|
||||||
- Git
|
|
||||||
- IntelliJ
|
|
||||||
- Java 8 JDK
|
|
||||||
- Account on the Magicbane [Public Repository](http://repo.magicbane.com)
|
|
||||||
|
|
||||||
|
|
||||||
Copy the HTTP link if you haven't yet installed a public key:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
- Clone the Magicbane public repo to your local machine using the copied URL.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
- Under Settings->VersionControl->Git make sure to turn off these two settings.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
- Select the Project Structure settings within the IDE.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
- Select Java 8 as the IDE target as shown.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
- Delete and recreate content root pointing at the **Server** directory.
|
|
||||||
- Make sure the Language Level still reflects Java 8.
|
|
||||||
- The IDE should now autodetect the cloned source.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Magicbane currently has the following dependencies.
|
|
||||||
<br>
|
|
||||||
|
|
||||||
- [EnumBitSet](https://github.com/claudemartin/enum-bit-set)
|
|
||||||
- [HikariCP](https://github.com/brettwooldridge/HikariCP)
|
|
||||||
- [JDA](https://github.com/DV8FromTheWorld/JDA)
|
|
||||||
- [JodaTime](https://github.com/JodaOrg/joda-time)
|
|
||||||
- [TinyLog](https://github.com/tinylog-org/tinylog/tree/v1.3)
|
|
||||||
- [MySqlConnector](https://dev.mysql.com/downloads/connector/j/)
|
|
||||||
|
|
||||||
They are all directly obtainable from a running MagicBox instance.
|
|
||||||
|
|
||||||
``` docker cp magicbox:/usr/share/java/EnumBitSet.jar Dependencies/```
|
|
||||||
|
|
||||||
Add the jar files as project libraries as shown.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
You should now be able to build the game!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Next Steps
|
|
||||||
|
|
||||||
You can now move onto the [Development Workflow](http://repo.magicbane.com/MagicBane/Server/wiki/Development-workflow) or [MagicBox Setup](http://repo.magicbane.com/MagicBane/Server/wiki/MagicBox-:-Magicbane-in-a-Box) pages for further information.
|
|
||||||
|
|
||||||
Support is also available through the Magicbane [Discord server](www.magicbane.com). Feel free to stop in and pick Magicbot's brain!
|
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
package discord;
|
package discord;
|
||||||
|
|
||||||
import discord.handlers.*;
|
import discord.handlers.*;
|
||||||
import engine.Enum;
|
|
||||||
import engine.gameManager.ConfigManager;
|
import engine.gameManager.ConfigManager;
|
||||||
import net.dv8tion.jda.api.JDA;
|
import net.dv8tion.jda.api.JDA;
|
||||||
import net.dv8tion.jda.api.JDABuilder;
|
import net.dv8tion.jda.api.JDABuilder;
|
||||||
@@ -29,8 +28,6 @@ import org.pmw.tinylog.writers.RollingFileWriter;
|
|||||||
|
|
||||||
import javax.security.auth.login.LoginException;
|
import javax.security.auth.login.LoginException;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -2416,9 +2416,9 @@ public class Enum {
|
|||||||
|
|
||||||
public enum CityBoundsType {
|
public enum CityBoundsType {
|
||||||
|
|
||||||
GRID(576),
|
GRID(512),
|
||||||
ZONE(640),
|
ZONE(576),
|
||||||
SIEGE(814);
|
SIEGE(1040);
|
||||||
|
|
||||||
public final float extents;
|
public final float extents;
|
||||||
|
|
||||||
|
|||||||
@@ -1130,7 +1130,6 @@ public class MobileFSM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HashMap<Integer, Integer> staticPowers = aiAgent.getMobBase().getStaticPowers();
|
HashMap<Integer, Integer> staticPowers = aiAgent.getMobBase().getStaticPowers();
|
||||||
|
|
||||||
if (staticPowers != null && !staticPowers.isEmpty()) {
|
if (staticPowers != null && !staticPowers.isEmpty()) {
|
||||||
|
|
||||||
int chance = ThreadLocalRandom.current().nextInt(100);
|
int chance = ThreadLocalRandom.current().nextInt(100);
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import engine.server.MBServerStatics;
|
|||||||
import engine.util.ThreadUtils;
|
import engine.util.ThreadUtils;
|
||||||
import org.pmw.tinylog.Logger;
|
import org.pmw.tinylog.Logger;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
|
|
||||||
public class MobileFSMManager {
|
public class MobileFSMManager {
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,19 @@ import engine.Enum.*;
|
|||||||
import engine.ai.MobileFSM.STATE;
|
import engine.ai.MobileFSM.STATE;
|
||||||
import engine.gameManager.ChatManager;
|
import engine.gameManager.ChatManager;
|
||||||
import engine.gameManager.CombatManager;
|
import engine.gameManager.CombatManager;
|
||||||
|
import engine.gameManager.PowersManager;
|
||||||
import engine.math.Vector3fImmutable;
|
import engine.math.Vector3fImmutable;
|
||||||
import engine.net.DispatchMessage;
|
import engine.net.DispatchMessage;
|
||||||
|
import engine.net.client.msg.PerformActionMsg;
|
||||||
import engine.net.client.msg.TargetedActionMsg;
|
import engine.net.client.msg.TargetedActionMsg;
|
||||||
import engine.objects.*;
|
import engine.objects.*;
|
||||||
|
import engine.powers.ActionsBase;
|
||||||
|
import engine.powers.PowersBase;
|
||||||
import engine.server.MBServerStatics;
|
import engine.server.MBServerStatics;
|
||||||
import org.pmw.tinylog.Logger;
|
import org.pmw.tinylog.Logger;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
@@ -237,7 +243,6 @@ public class CombatUtilities {
|
|||||||
speed = agent.getSpeedHandOne();
|
speed = agent.getSpeedHandOne();
|
||||||
else
|
else
|
||||||
speed = agent.getSpeedHandTwo();
|
speed = agent.getSpeedHandTwo();
|
||||||
|
|
||||||
DamageType dt = DamageType.Crush;
|
DamageType dt = DamageType.Crush;
|
||||||
if (agent.isSiege())
|
if (agent.isSiege())
|
||||||
dt = DamageType.Siege;
|
dt = DamageType.Siege;
|
||||||
@@ -271,15 +276,85 @@ public class CombatUtilities {
|
|||||||
|
|
||||||
int passiveAnim = CombatManager.getSwingAnimation(wb, null,mainHand);
|
int passiveAnim = CombatManager.getSwingAnimation(wb, null,mainHand);
|
||||||
if(canSwing(agent)) {
|
if(canSwing(agent)) {
|
||||||
if(triggerDefense(agent,target))
|
if(triggerDefense(agent,target)) {
|
||||||
swingIsMiss(agent,target, passiveAnim);
|
swingIsMiss(agent, target, passiveAnim);
|
||||||
else if(triggerDodge(agent,target))
|
return;
|
||||||
swingIsDodge(agent,target, passiveAnim);
|
}
|
||||||
else if(triggerParry(agent,target))
|
else if(triggerDodge(agent,target)) {
|
||||||
swingIsParry(agent,target, passiveAnim);
|
swingIsDodge(agent, target, passiveAnim);
|
||||||
else if(triggerBlock(agent,target))
|
return;
|
||||||
swingIsBlock(agent,target, passiveAnim);
|
}
|
||||||
|
else if (triggerParry(agent, target)){
|
||||||
|
swingIsParry(agent, target, passiveAnim);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(triggerBlock(agent,target)) {
|
||||||
|
swingIsBlock(agent, target, passiveAnim);
|
||||||
|
return;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
//check for a cast here?
|
||||||
|
|
||||||
|
//agent.mobPowers = DbManager.MobBaseQueries.LOAD_STATIC_POWERS(agent.getMobBaseID());
|
||||||
|
|
||||||
|
if(agent.mobPowers.size() > 0 && agent.mobPowers != null)
|
||||||
|
{
|
||||||
|
//get cast chance 33% cast 67% mele
|
||||||
|
int random = ThreadLocalRandom.current().nextInt(agent.mobPowers.size() * 10);
|
||||||
|
//allow casting of spell
|
||||||
|
if(random <= agent.mobPowers.size())
|
||||||
|
{
|
||||||
|
int powerToken;
|
||||||
|
int powerRank;
|
||||||
|
//cast a spell
|
||||||
|
Map<Integer,Integer> entries = agent.mobPowers;
|
||||||
|
int count = 0;
|
||||||
|
for(Map.Entry<Integer,Integer> entry : entries.entrySet())
|
||||||
|
{
|
||||||
|
count += 1;
|
||||||
|
if(count == random)
|
||||||
|
{
|
||||||
|
powerToken = entry.getKey();
|
||||||
|
//powerRank = entry.getValue();
|
||||||
|
switch(agent.getLevel())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
powerRank = 1;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
powerRank = 5;
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
powerRank = 10;
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
powerRank = 15;
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
powerRank = 25;
|
||||||
|
break;
|
||||||
|
case 50:
|
||||||
|
powerRank = 30;
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
powerRank = 35;
|
||||||
|
break;
|
||||||
|
case 70:
|
||||||
|
powerRank = 40;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//System.out.println(agent.getMobBase().getFirstName() + " is casting: " + PowersManager.getPowerByToken(powerToken).skillName);
|
||||||
|
PowersManager.applyPower(agent,target,target.getLoc(),powerToken,powerRank, false);
|
||||||
|
//PerformActionMsg msg = new PerformActionMsg();
|
||||||
|
//PowersManager.sendPowerMsg((PlayerCharacter)target,0,msg);
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//finished with casting check
|
||||||
swingIsDamage(agent,target, determineDamage(agent,target, mainHand, speed, dt), anim);
|
swingIsDamage(agent,target, determineDamage(agent,target, mainHand, speed, dt), anim);
|
||||||
|
|
||||||
if (agent.getWeaponPower() != null)
|
if (agent.getWeaponPower() != null)
|
||||||
@@ -380,11 +455,46 @@ public class CombatUtilities {
|
|||||||
public static float determineDamage(Mob agent,AbstractWorldObject target, boolean mainHand, float speed, DamageType dt) {
|
public static float determineDamage(Mob agent,AbstractWorldObject target, boolean mainHand, float speed, DamageType dt) {
|
||||||
|
|
||||||
float min = (mainHand) ? agent.getMinDamageHandOne() : agent.getMinDamageHandTwo();
|
float min = (mainHand) ? agent.getMinDamageHandOne() : agent.getMinDamageHandTwo();
|
||||||
float max = (mainHand) ? agent.getMaxDamageHandOne() : agent.getMaxDamageHandTwo();;
|
float max = (mainHand) ? agent.getMaxDamageHandOne() : agent.getMaxDamageHandTwo();
|
||||||
|
if(agent.isSummonedPet() == true)
|
||||||
|
{
|
||||||
|
min = 40 * (1 + (agent.getLevel()/10));
|
||||||
|
max = 60 * (1 + (agent.getLevel()/8));
|
||||||
|
//check if we have powers to cast
|
||||||
|
if(agent.mobPowers.isEmpty() == false) {
|
||||||
|
//check for power usage
|
||||||
|
Random random = new Random();
|
||||||
|
int value = random.nextInt(0 + (agent.mobPowers.size() + (agent.mobPowers.size() * 5))) + 0;
|
||||||
|
if (value <= agent.mobPowers.size())
|
||||||
|
{
|
||||||
|
//do power
|
||||||
|
int powerId = agent.mobPowers.get(value);
|
||||||
|
PowersManager.runPowerAction(agent,target,target.getLoc(),new ActionsBase(),40, PowersManager.getPowerByToken(powerId));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do mele damage
|
||||||
|
float range = max - min;
|
||||||
|
float damage = min + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2;
|
||||||
|
if (AbstractWorldObject.IsAbstractCharacter(target))
|
||||||
|
if (((AbstractCharacter) target).isSit())
|
||||||
|
damage *= 2.5f; //increase damage if sitting
|
||||||
|
|
||||||
|
if (AbstractWorldObject.IsAbstractCharacter(target))
|
||||||
|
return ((AbstractCharacter) target).getResists().getResistedDamage(agent, (AbstractCharacter) target, dt, damage, 0);
|
||||||
|
|
||||||
|
if (target.getObjectType() == GameObjectType.Building) {
|
||||||
|
Building building = (Building) target;
|
||||||
|
Resists resists = building.getResists();
|
||||||
|
return damage * (1 - (resists.getResist(dt, 0) / 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
float range = max - min;
|
float range = max - min;
|
||||||
float damage = min + ((ThreadLocalRandom.current().nextFloat()*range)+(ThreadLocalRandom.current().nextFloat()*range))/2;
|
float damage = min + ((ThreadLocalRandom.current().nextFloat()*range)+(ThreadLocalRandom.current().nextFloat()*range))/2;
|
||||||
|
//DAMAGE FORMULA FOR PET
|
||||||
if (AbstractWorldObject.IsAbstractCharacter(target))
|
if (AbstractWorldObject.IsAbstractCharacter(target))
|
||||||
if (((AbstractCharacter)target).isSit())
|
if (((AbstractCharacter)target).isSit())
|
||||||
damage *= 2.5f; //increase damage if sitting
|
damage *= 2.5f; //increase damage if sitting
|
||||||
@@ -404,10 +514,7 @@ public class CombatUtilities {
|
|||||||
|
|
||||||
public static boolean RunAIRandom(){
|
public static boolean RunAIRandom(){
|
||||||
int random = ThreadLocalRandom.current().nextInt(4);
|
int random = ThreadLocalRandom.current().nextInt(4);
|
||||||
|
|
||||||
if (random == 0)
|
return random == 0;
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class DataWarehouse implements Runnable {
|
|||||||
// If WarehousePush is disabled
|
// If WarehousePush is disabled
|
||||||
// then early exit
|
// then early exit
|
||||||
|
|
||||||
if ( ConfigManager.MB_WORLD_WAREHOUSE_PUSH.getValue().equals("false")) {
|
if ( ConfigManager.MB_WORLD_WAREHOUSE_PUSH.getValue().equalsIgnoreCase("false")) {
|
||||||
Logger.info("Warehouse Remote Connection disabled along with push");
|
Logger.info("Warehouse Remote Connection disabled along with push");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.pmw.tinylog.Logger;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public enum ConfigManager {
|
public enum ConfigManager {
|
||||||
|
|
||||||
@@ -47,6 +48,8 @@ public enum ConfigManager {
|
|||||||
|
|
||||||
MB_LOGIN_PORT,
|
MB_LOGIN_PORT,
|
||||||
MB_LOGIN_AUTOREG,
|
MB_LOGIN_AUTOREG,
|
||||||
|
MB_LOGIN_FNAME_REGEX,
|
||||||
|
|
||||||
MB_MAJOR_VER,
|
MB_MAJOR_VER,
|
||||||
MB_MINOR_VER,
|
MB_MINOR_VER,
|
||||||
|
|
||||||
@@ -87,6 +90,7 @@ public enum ConfigManager {
|
|||||||
public static NetMsgHandler handler;
|
public static NetMsgHandler handler;
|
||||||
public static WorldServer worldServer;
|
public static WorldServer worldServer;
|
||||||
public static LoginServer loginServer;
|
public static LoginServer loginServer;
|
||||||
|
public static Map<ConfigManager, Pattern> regex = new HashMap<>();
|
||||||
|
|
||||||
// Called at bootstrap: ensures that all config values are loaded.
|
// Called at bootstrap: ensures that all config values are loaded.
|
||||||
|
|
||||||
@@ -99,9 +103,15 @@ public enum ConfigManager {
|
|||||||
Logger.info(configSetting.name() + ":" + configSetting.getValue());
|
Logger.info(configSetting.name() + ":" + configSetting.getValue());
|
||||||
else {
|
else {
|
||||||
Logger.error("Missing Config: " + configSetting.name());
|
Logger.error("Missing Config: " + configSetting.name());
|
||||||
|
Logger.error("This codebase requires >= MagicBox v1.3");
|
||||||
|
Logger.error("docker pull magicbane/magicbox:latest");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compile regex here
|
||||||
|
|
||||||
|
regex.put(MB_LOGIN_FNAME_REGEX, Pattern.compile(MB_LOGIN_FNAME_REGEX.getValue()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ public enum MaintenanceManager {
|
|||||||
|
|
||||||
// Run maintenance on player buildings
|
// Run maintenance on player buildings
|
||||||
|
|
||||||
if ((boolean) ConfigManager.MB_WORLD_MAINTENANCE.getValue().equals("true"))
|
if ((boolean) ConfigManager.MB_WORLD_MAINTENANCE.getValue().equalsIgnoreCase("true"))
|
||||||
processBuildingMaintenance();
|
processBuildingMaintenance();
|
||||||
else
|
else
|
||||||
Logger.info("Maintenance Costings: DISABLED");
|
Logger.info("Maintenance Costings: DISABLED");
|
||||||
|
|||||||
@@ -15,16 +15,11 @@ import engine.Enum.SourceType;
|
|||||||
import engine.InterestManagement.InterestManager;
|
import engine.InterestManagement.InterestManager;
|
||||||
import engine.InterestManagement.WorldGrid;
|
import engine.InterestManagement.WorldGrid;
|
||||||
import engine.exception.MsgSendException;
|
import engine.exception.MsgSendException;
|
||||||
import engine.job.JobContainer;
|
|
||||||
import engine.job.JobScheduler;
|
|
||||||
import engine.jobs.ChangeAltitudeJob;
|
|
||||||
import engine.jobs.FlightJob;
|
|
||||||
import engine.math.Bounds;
|
import engine.math.Bounds;
|
||||||
import engine.math.Vector3f;
|
import engine.math.Vector3f;
|
||||||
import engine.math.Vector3fImmutable;
|
import engine.math.Vector3fImmutable;
|
||||||
import engine.net.DispatchMessage;
|
import engine.net.DispatchMessage;
|
||||||
import engine.net.client.ClientConnection;
|
import engine.net.client.ClientConnection;
|
||||||
import engine.net.client.msg.ChangeAltitudeMsg;
|
|
||||||
import engine.net.client.msg.MoveToPointMsg;
|
import engine.net.client.msg.MoveToPointMsg;
|
||||||
import engine.net.client.msg.TeleportToPointMsg;
|
import engine.net.client.msg.TeleportToPointMsg;
|
||||||
import engine.net.client.msg.UpdateStateMsg;
|
import engine.net.client.msg.UpdateStateMsg;
|
||||||
@@ -339,61 +334,7 @@ public enum MovementManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update for when the character is in flight
|
|
||||||
public static void updateFlight(PlayerCharacter pc, ChangeAltitudeMsg msg, int duration) {
|
|
||||||
if (pc == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// clear flight timer job as we are about to update stuff and submit a new job
|
|
||||||
pc.clearTimer(flightTimerJobName);
|
|
||||||
|
|
||||||
if (!pc.isActive()) {
|
|
||||||
pc.setAltitude(0);
|
|
||||||
pc.setDesiredAltitude(0);
|
|
||||||
pc.setTakeOffTime(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see if we are mid height change
|
|
||||||
JobContainer cjc = pc.getTimers().get(changeAltitudeTimerJobName);
|
|
||||||
if (cjc != null) {
|
|
||||||
addFlightTimer(pc, msg, MBServerStatics.FLY_FREQUENCY_MS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Altitude is zero, do nothing
|
|
||||||
if (pc.getAltitude() < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
//make sure player is still allowed to fly
|
|
||||||
boolean canFly = false;
|
|
||||||
PlayerBonuses bonus = pc.getBonuses();
|
|
||||||
|
|
||||||
if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.Fly) && bonus.getBool(ModType.Fly, SourceType.None) && pc.isAlive())
|
|
||||||
canFly = true;
|
|
||||||
|
|
||||||
// if stam less that 2 - time to force a landing
|
|
||||||
if (pc.getStamina() < 10f || !canFly) {
|
|
||||||
|
|
||||||
// dont call stop movement here as we want to
|
|
||||||
// preserve endloc
|
|
||||||
//pc.stopMovement();
|
|
||||||
// sync world location
|
|
||||||
pc.setLoc(pc.getLoc());
|
|
||||||
// force a landing
|
|
||||||
msg.setStartAlt(pc.getAltitude());
|
|
||||||
msg.setTargetAlt(0);
|
|
||||||
msg.setAmountToMove(pc.getAltitude());
|
|
||||||
msg.setUp(false);
|
|
||||||
DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
|
|
||||||
MovementManager.addChangeAltitudeTimer(pc, msg.getStartAlt(), msg.getTargetAlt(), (int) (MBServerStatics.HEIGHT_CHANGE_TIMER_MS * pc.getAltitude()));
|
|
||||||
pc.setAltitude(msg.getStartAlt() - 10);
|
|
||||||
|
|
||||||
} else //Add a new flight timer to check stam / force land
|
|
||||||
if (pc.getAltitude() > 0)
|
|
||||||
addFlightTimer(pc, msg, MBServerStatics.FLY_FREQUENCY_MS);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void finishChangeAltitude(AbstractCharacter ac, float targetAlt) {
|
public static void finishChangeAltitude(AbstractCharacter ac, float targetAlt) {
|
||||||
|
|
||||||
@@ -529,27 +470,6 @@ public enum MovementManager {
|
|||||||
member.setEndLoc(destination);
|
member.setEndLoc(destination);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Getting rid of flgith timer.
|
|
||||||
|
|
||||||
public static void addFlightTimer(PlayerCharacter pc, ChangeAltitudeMsg msg, int duration) {
|
|
||||||
if (pc == null || pc.getTimers() == null)
|
|
||||||
return;
|
|
||||||
if (!pc.getTimers().containsKey(flightTimerJobName)) {
|
|
||||||
FlightJob ftj = new FlightJob(pc, msg, duration);
|
|
||||||
JobContainer jc = JobScheduler.getInstance().scheduleJob(ftj, duration);
|
|
||||||
pc.getTimers().put(flightTimerJobName, jc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addChangeAltitudeTimer(PlayerCharacter pc, float startAlt, float targetAlt, int duration) {
|
|
||||||
if (pc == null || pc.getTimers() == null)
|
|
||||||
return;
|
|
||||||
ChangeAltitudeJob catj = new ChangeAltitudeJob(pc, startAlt, targetAlt);
|
|
||||||
JobContainer jc = JobScheduler.getInstance().scheduleJob(catj, duration);
|
|
||||||
pc.getTimers().put(changeAltitudeTimerJobName, jc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void translocate(AbstractCharacter teleporter, Vector3fImmutable targetLoc, Regions region) {
|
public static void translocate(AbstractCharacter teleporter, Vector3fImmutable targetLoc, Regions region) {
|
||||||
|
|
||||||
@@ -557,15 +477,10 @@ public enum MovementManager {
|
|||||||
if (targetLoc == null)
|
if (targetLoc == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
Vector3fImmutable oldLoc = new Vector3fImmutable(teleporter.getLoc());
|
Vector3fImmutable oldLoc = new Vector3fImmutable(teleporter.getLoc());
|
||||||
|
|
||||||
|
|
||||||
teleporter.stopMovement(targetLoc);
|
teleporter.stopMovement(targetLoc);
|
||||||
|
|
||||||
teleporter.setRegion(region);
|
teleporter.setRegion(region);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//mobs ignore region sets for now.
|
//mobs ignore region sets for now.
|
||||||
if (teleporter.getObjectType().equals(GameObjectType.Mob)){
|
if (teleporter.getObjectType().equals(GameObjectType.Mob)){
|
||||||
@@ -588,17 +503,11 @@ public enum MovementManager {
|
|||||||
|
|
||||||
public static void translocateToObject(AbstractCharacter teleporter, AbstractWorldObject worldObject) {
|
public static void translocateToObject(AbstractCharacter teleporter, AbstractWorldObject worldObject) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Vector3fImmutable targetLoc = teleporter.getLoc();
|
Vector3fImmutable targetLoc = teleporter.getLoc();
|
||||||
|
|
||||||
Vector3fImmutable oldLoc = new Vector3fImmutable(teleporter.getLoc());
|
Vector3fImmutable oldLoc = new Vector3fImmutable(teleporter.getLoc());
|
||||||
|
|
||||||
teleporter.stopMovement(teleporter.getLoc());
|
teleporter.stopMovement(teleporter.getLoc());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//mobs ignore region sets for now.
|
//mobs ignore region sets for now.
|
||||||
if (teleporter.getObjectType().equals(GameObjectType.Mob)){
|
if (teleporter.getObjectType().equals(GameObjectType.Mob)){
|
||||||
@@ -609,6 +518,7 @@ public enum MovementManager {
|
|||||||
DispatchMessage.dispatchMsgToInterestArea(oldLoc, teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
|
DispatchMessage.dispatchMsgToInterestArea(oldLoc, teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean collide = false;
|
boolean collide = false;
|
||||||
int maxFloor = -1;
|
int maxFloor = -1;
|
||||||
int buildingID = 0;
|
int buildingID = 0;
|
||||||
@@ -619,6 +529,7 @@ public enum MovementManager {
|
|||||||
if (collide)
|
if (collide)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!collide) {
|
if (!collide) {
|
||||||
teleporter.setInBuildingID(0);
|
teleporter.setInBuildingID(0);
|
||||||
teleporter.setInBuilding(-1);
|
teleporter.setInBuilding(-1);
|
||||||
@@ -633,7 +544,6 @@ public enum MovementManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TeleportToPointMsg msg = new TeleportToPointMsg(teleporter, targetLoc.getX(), targetLoc.getY(), targetLoc.getZ(), 0, -1, -1);
|
TeleportToPointMsg msg = new TeleportToPointMsg(teleporter, targetLoc.getX(), targetLoc.getY(), targetLoc.getZ(), 0, -1, -1);
|
||||||
//we shouldnt need to send teleport message to new area, as loadjob should pick it up.
|
//we shouldnt need to send teleport message to new area, as loadjob should pick it up.
|
||||||
// DispatchMessage.dispatchMsgToInterestArea(teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
|
// DispatchMessage.dispatchMsgToInterestArea(teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
||||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
||||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
||||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
||||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
||||||
// Magicbane Emulator Project © 2013 - 2022
|
|
||||||
// www.magicbane.com
|
|
||||||
|
|
||||||
|
|
||||||
package engine.jobs;
|
|
||||||
|
|
||||||
import engine.gameManager.MovementManager;
|
|
||||||
import engine.job.AbstractScheduleJob;
|
|
||||||
import engine.net.client.msg.ChangeAltitudeMsg;
|
|
||||||
import engine.objects.PlayerCharacter;
|
|
||||||
|
|
||||||
public class FlightJob extends AbstractScheduleJob {
|
|
||||||
|
|
||||||
private final PlayerCharacter pc;
|
|
||||||
private final ChangeAltitudeMsg msg;
|
|
||||||
private final int duration;
|
|
||||||
|
|
||||||
public FlightJob(PlayerCharacter pc, ChangeAltitudeMsg msg, int duration) {
|
|
||||||
super();
|
|
||||||
this.msg = msg;
|
|
||||||
this.duration = duration;
|
|
||||||
this.pc = pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doJob() {
|
|
||||||
if (this.pc != null && this.msg != null)
|
|
||||||
MovementManager.updateFlight(pc, msg, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void _cancelJob() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -92,7 +92,23 @@ public class AbandonAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
// Trees require special handling beyond an individual building
|
// Trees require special handling beyond an individual building
|
||||||
if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.TOL))
|
if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.TOL))
|
||||||
|
{
|
||||||
|
// CHECK IF GUILD HAS A BANE DROPPED
|
||||||
|
City city = ZoneManager.getCityAtLocation(building.getLoc());
|
||||||
|
if(city.getGuild().getSubGuildList().isEmpty() == false)
|
||||||
|
{
|
||||||
|
//nations cant abandon their tree
|
||||||
|
ErrorPopupMsg.sendErrorMsg(player, "Nations Cannot Abandon Their Capital!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(Bane.getBaneByAttackerGuild(city.getGuild()) != null)
|
||||||
|
{
|
||||||
|
ErrorPopupMsg.sendErrorMsg(player, "You Cannot Abandon Your Tree With An Active Siege!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
AbandonAllCityObjects(player, building);
|
AbandonAllCityObjects(player, building);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
AbandonSingleAsset(player, building);
|
AbandonSingleAsset(player, building);
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,8 @@ import engine.gameManager.BuildingManager;
|
|||||||
import engine.net.client.ClientConnection;
|
import engine.net.client.ClientConnection;
|
||||||
import engine.net.client.msg.ClientNetMsg;
|
import engine.net.client.msg.ClientNetMsg;
|
||||||
import engine.net.client.msg.DestroyBuildingMsg;
|
import engine.net.client.msg.DestroyBuildingMsg;
|
||||||
import engine.objects.Blueprint;
|
import engine.net.client.msg.ErrorPopupMsg;
|
||||||
import engine.objects.Building;
|
import engine.objects.*;
|
||||||
import engine.objects.City;
|
|
||||||
import engine.objects.PlayerCharacter;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @Author:
|
* @Author:
|
||||||
@@ -43,7 +41,7 @@ public class DestroyBuildingHandler extends AbstractClientMsgHandler {
|
|||||||
Blueprint blueprint;
|
Blueprint blueprint;
|
||||||
|
|
||||||
blueprint = building.getBlueprint();
|
blueprint = building.getBlueprint();
|
||||||
|
City city = building.getCity();
|
||||||
// Can't destroy buildings without a blueprint.
|
// Can't destroy buildings without a blueprint.
|
||||||
|
|
||||||
if (blueprint == null)
|
if (blueprint == null)
|
||||||
@@ -57,7 +55,11 @@ public class DestroyBuildingHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
if (!BuildingManager.PlayerCanControlNotOwner(building, pc))
|
if (!BuildingManager.PlayerCanControlNotOwner(building, pc))
|
||||||
return true;
|
return true;
|
||||||
|
Bane bane = city.getBane();
|
||||||
|
if(bane.getSiegePhase() == Enum.SiegePhase.WAR && bane != null) {
|
||||||
|
ErrorPopupMsg.sendErrorPopup(pc, 171);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Can't destroy a tree of life
|
// Can't destroy a tree of life
|
||||||
if (blueprint.getBuildingGroup() == BuildingGroup.TOL)
|
if (blueprint.getBuildingGroup() == BuildingGroup.TOL)
|
||||||
return true;
|
return true;
|
||||||
@@ -71,15 +73,12 @@ public class DestroyBuildingHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
if (blueprint.getBuildingGroup() == BuildingGroup.RUNEGATE)
|
if (blueprint.getBuildingGroup() == BuildingGroup.RUNEGATE)
|
||||||
return true;
|
return true;
|
||||||
|
//stop if active siege
|
||||||
// Turn off spire if destoying
|
// Turn off spire if destoying
|
||||||
if (blueprint.getBuildingGroup() == BuildingGroup.SPIRE)
|
if (blueprint.getBuildingGroup() == BuildingGroup.SPIRE)
|
||||||
building.disableSpire(true);
|
building.disableSpire(true);
|
||||||
|
|
||||||
if (blueprint.getBuildingGroup() == BuildingGroup.WAREHOUSE) {
|
if (blueprint.getBuildingGroup() == BuildingGroup.WAREHOUSE) {
|
||||||
|
|
||||||
City city = building.getCity();
|
|
||||||
|
|
||||||
if (city != null)
|
if (city != null)
|
||||||
city.setWarehouseBuildingID(0);
|
city.setWarehouseBuildingID(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
if (buildingList == null) {
|
if (buildingList == null) {
|
||||||
Logger.error("Player " + playerCharacter.getCombinedName()
|
Logger.error("Player " + playerCharacter.getCombinedName()
|
||||||
+ " null building list on deed use");
|
+ " null building list on deed use");
|
||||||
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
|
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
|
||||||
closePlaceAssetWindow(origin);
|
closePlaceAssetWindow(origin);
|
||||||
return true;
|
return true;
|
||||||
@@ -115,7 +115,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
if (buildingBlueprint == null) {
|
if (buildingBlueprint == null) {
|
||||||
Logger.error("Player " + playerCharacter.getCombinedName()
|
Logger.error("Player " + playerCharacter.getCombinedName()
|
||||||
+ " null blueprint UUID: " + buildingList.getBlueprintUUID() + " on deed use");
|
+ " null blueprint UUID: " + buildingList.getBlueprintUUID() + " on deed use");
|
||||||
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
|
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
|
||||||
closePlaceAssetWindow(origin);
|
closePlaceAssetWindow(origin);
|
||||||
return true;
|
return true;
|
||||||
@@ -130,66 +130,66 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
boolean close = true;
|
boolean close = true;
|
||||||
lock.writeLock().lock();
|
lock.writeLock().lock();
|
||||||
|
boolean isSiege = false;
|
||||||
try {
|
try {
|
||||||
switch (buildingBlueprint.getBuildingGroup()) {
|
switch (buildingBlueprint.getBuildingGroup()) {
|
||||||
|
|
||||||
case TOL:
|
case TOL:
|
||||||
if (contract == null)
|
if (contract == null)
|
||||||
|
break;
|
||||||
|
buildingCreated = placeTreeOfLife(playerCharacter, origin, msg);
|
||||||
break;
|
break;
|
||||||
buildingCreated = placeTreeOfLife(playerCharacter, origin, msg);
|
case WAREHOUSE:
|
||||||
break;
|
if (contract == null)
|
||||||
case WAREHOUSE:
|
break;
|
||||||
if (contract == null)
|
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
||||||
|
break;
|
||||||
|
buildingCreated = placeWarehouse(playerCharacter, origin, msg);
|
||||||
break;
|
break;
|
||||||
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
case SIEGETENT:
|
||||||
|
case BULWARK:
|
||||||
|
if (contract == null)
|
||||||
|
break;
|
||||||
|
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
||||||
|
break;
|
||||||
|
buildingCreated = placeSiegeEquip(playerCharacter, origin, msg);
|
||||||
break;
|
break;
|
||||||
buildingCreated = placeWarehouse(playerCharacter, origin, msg);
|
case SPIRE:
|
||||||
break;
|
if (contract == null)
|
||||||
case SIEGETENT:
|
break;
|
||||||
case BULWARK:
|
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
||||||
if (contract == null)
|
break;
|
||||||
|
buildingCreated = placeSpire(playerCharacter, origin, msg);
|
||||||
break;
|
break;
|
||||||
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
case SHRINE:
|
||||||
|
if (contract == null)
|
||||||
|
break;
|
||||||
|
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
||||||
|
break;
|
||||||
|
buildingCreated = placeShrine(playerCharacter, origin, msg);
|
||||||
break;
|
break;
|
||||||
buildingCreated = placeSiegeEquip(playerCharacter, origin, msg);
|
case BARRACK:
|
||||||
break;
|
if (contract == null)
|
||||||
case SPIRE:
|
break;
|
||||||
if (contract == null)
|
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
||||||
|
break;
|
||||||
|
buildingCreated = placeBarrack(playerCharacter, origin, msg);
|
||||||
break;
|
break;
|
||||||
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
case WALLSTRAIGHT:
|
||||||
|
case WALLCORNER:
|
||||||
|
case SMALLGATE:
|
||||||
|
case ARTYTOWER:
|
||||||
|
case WALLSTAIRS:
|
||||||
|
buildingCreated = placeCityWalls(playerCharacter, origin, msg);
|
||||||
|
close = false;
|
||||||
break;
|
break;
|
||||||
buildingCreated = placeSpire(playerCharacter, origin, msg);
|
default:
|
||||||
break;
|
if (contract == null)
|
||||||
case SHRINE:
|
break;
|
||||||
if (contract == null)
|
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
||||||
|
break;
|
||||||
|
buildingCreated = placeSingleBuilding(playerCharacter, origin, msg);
|
||||||
break;
|
break;
|
||||||
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
|
||||||
break;
|
|
||||||
buildingCreated = placeShrine(playerCharacter, origin, msg);
|
|
||||||
break;
|
|
||||||
case BARRACK:
|
|
||||||
if (contract == null)
|
|
||||||
break;
|
|
||||||
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
|
||||||
break;
|
|
||||||
buildingCreated = placeBarrack(playerCharacter, origin, msg);
|
|
||||||
break;
|
|
||||||
case WALLSTRAIGHT:
|
|
||||||
case WALLCORNER:
|
|
||||||
case SMALLGATE:
|
|
||||||
case ARTYTOWER:
|
|
||||||
case WALLSTAIRS:
|
|
||||||
buildingCreated = placeCityWalls(playerCharacter, origin, msg);
|
|
||||||
close = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (contract == null)
|
|
||||||
break;
|
|
||||||
if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID()))
|
|
||||||
break;
|
|
||||||
buildingCreated = placeSingleBuilding(playerCharacter, origin, msg);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.error("PlaceAssetHandler", e.getMessage());
|
Logger.error("PlaceAssetHandler", e.getMessage());
|
||||||
@@ -230,7 +230,6 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
buildingList = msg.getFirstPlacementInfo();
|
buildingList = msg.getFirstPlacementInfo();
|
||||||
|
|
||||||
serverZone = ZoneManager.findSmallestZone(buildingList.getLoc());
|
serverZone = ZoneManager.findSmallestZone(buildingList.getLoc());
|
||||||
|
|
||||||
// Early exit if something went horribly wrong
|
// Early exit if something went horribly wrong
|
||||||
// with locating the current or zone
|
// with locating the current or zone
|
||||||
|
|
||||||
@@ -346,125 +345,125 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
City attackerCity = null;
|
City attackerCity = null;
|
||||||
if (bane != null)
|
if (bane != null)
|
||||||
attackerCity = bane.getCity();
|
attackerCity = bane.getCity();
|
||||||
|
|
||||||
if (attackerCity != null)
|
if (attackerCity != null)
|
||||||
if (buildingList.getLoc().isInsideCircle(attackerCity.getLoc(), Enum.CityBoundsType.SIEGE.extents))
|
if (buildingList.getLoc().isInsideCircle(attackerCity.getLoc(), Enum.CityBoundsType.SIEGE.extents))
|
||||||
serverCity = attackerCity;
|
serverCity = attackerCity;
|
||||||
}
|
}
|
||||||
//no city found for attacker city,
|
//no city found for attacker city,
|
||||||
//check if defender city
|
//check if defender city
|
||||||
|
|
||||||
if (serverCity == null){
|
if (serverCity == null){
|
||||||
if (player.getGuild().getOwnedCity() != null)
|
if (player.getGuild().getOwnedCity() != null)
|
||||||
if (buildingList.getLoc().isInsideCircle(player.getGuild().getOwnedCity().getLoc(), Enum.CityBoundsType.SIEGE.extents))
|
if (buildingList.getLoc().isInsideCircle(player.getGuild().getOwnedCity().getLoc(), Enum.CityBoundsType.SIEGE.extents))
|
||||||
serverCity = player.getGuild().getOwnedCity();
|
serverCity = player.getGuild().getOwnedCity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ((serverCity != null) &&
|
if ((serverCity != null) &&
|
||||||
(serverCity.getBane() != null)) {
|
(serverCity.getBane() != null)) {
|
||||||
|
|
||||||
// Set the server zone to the city zone in order to account for being inside
|
// Set the server zone to the city zone in order to account for being inside
|
||||||
// the siege bounds buffer area
|
// the siege bounds buffer area
|
||||||
|
|
||||||
serverZone = serverCity.getParent();
|
serverZone = serverCity.getParent();
|
||||||
|
|
||||||
if ((player.getGuild().equals(serverCity.getBane().getOwner().getGuild()) == false)
|
if ((player.getGuild().equals(serverCity.getBane().getOwner().getGuild()) == false)
|
||||||
&& (player.getGuild().equals(serverCity.getGuild()) == false)) {
|
&& (player.getGuild().equals(serverCity.getGuild()) == false)) {
|
||||||
PlaceAssetMsg.sendPlaceAssetError(origin, 54, ""); // Must belong to attacker or defender
|
PlaceAssetMsg.sendPlaceAssetError(origin, 54, ""); // Must belong to attacker or defender
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cant place siege equipment off city zone.
|
// cant place siege equipment off city zone.
|
||||||
|
|
||||||
|
|
||||||
// Create the siege Building
|
// Create the siege Building
|
||||||
|
|
||||||
siegeBuilding = createStructure(player, msg.getFirstPlacementInfo(), serverZone);
|
siegeBuilding = createStructure(player, msg.getFirstPlacementInfo(), serverZone);
|
||||||
if (serverCity == null)
|
if (serverCity == null)
|
||||||
return true;
|
return true;
|
||||||
// Oops something went really wrong
|
// Oops something went really wrong
|
||||||
|
|
||||||
if (siegeBuilding == null)
|
if (siegeBuilding == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
if (serverCity.getBane() == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
if (serverCity.getBane() == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// If there is an bane placed, we protect 2x the stone rank's worth of attacker assets
|
// If there is an bane placed, we protect 2x the stone rank's worth of attacker assets
|
||||||
// and 1x the tree rank's worth of assets automatically
|
// and 1x the tree rank's worth of assets automatically
|
||||||
|
|
||||||
HashSet<AbstractWorldObject> awoList = WorldGrid.getObjectsInRangePartial(serverCity, 1000, MBServerStatics.MASK_BUILDING);
|
|
||||||
|
|
||||||
|
HashSet<AbstractWorldObject> awoList = WorldGrid.getObjectsInRangePartial(serverCity, 1000, MBServerStatics.MASK_BUILDING);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (AbstractWorldObject awo : awoList) {
|
for (AbstractWorldObject awo : awoList) {
|
||||||
Building building = (Building)awo;
|
Building building = (Building)awo;
|
||||||
|
|
||||||
if (building.getBlueprint() != null)
|
if (building.getBlueprint() != null)
|
||||||
if (!building.getBlueprint().isSiegeEquip())
|
if (!building.getBlueprint().isSiegeEquip())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!building.getLoc().isInsideCircle(serverCity.getLoc(), Enum.CityBoundsType.SIEGE.extents))
|
if (!building.getLoc().isInsideCircle(serverCity.getLoc(), Enum.CityBoundsType.SIEGE.extents))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (building.getGuild() == null)
|
if (building.getGuild() == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (building.getGuild().isErrant())
|
if (building.getGuild().isErrant())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
if (!building.getGuild().equals(serverCity.getGuild()) && !building.getGuild().equals(serverCity.getBane().getOwner().getGuild()))
|
if (!building.getGuild().equals(serverCity.getGuild()) && !building.getGuild().equals(serverCity.getBane().getOwner().getGuild()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Only count auto protected buildings
|
// Only count auto protected buildings
|
||||||
if (building.getProtectionState() != ProtectionState.PROTECTED)
|
if (building.getProtectionState() != ProtectionState.PROTECTED)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (building.getGuild().equals(serverCity.getGuild()))
|
||||||
|
numDefenderBuildings++;
|
||||||
|
else
|
||||||
|
if (building.getGuild().equals(serverCity.getBane().getOwner().getGuild()))
|
||||||
|
numAttackerBuildings++;
|
||||||
|
|
||||||
if (building.getGuild().equals(serverCity.getGuild()))
|
|
||||||
numDefenderBuildings++;
|
|
||||||
else
|
|
||||||
if (building.getGuild().equals(serverCity.getBane().getOwner().getGuild()))
|
|
||||||
numAttackerBuildings++;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate bane limits on siege assets
|
// Validate bane limits on siege assets
|
||||||
|
|
||||||
if (serverCity.getBane() != null)
|
if (serverCity.getBane() != null)
|
||||||
if ((player.getGuild().equals(serverCity.getBane().getOwner().getGuild())) &&
|
if ((player.getGuild().equals(serverCity.getBane().getOwner().getGuild())) &&
|
||||||
(numAttackerBuildings >= serverCity.getBane().getStone().getRank() * 2)) {
|
(numAttackerBuildings >= serverCity.getBane().getStone().getRank() * 2)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((player.getGuild().equals(serverCity.getGuild())) &&
|
if ((player.getGuild().equals(serverCity.getGuild())) &&
|
||||||
(numDefenderBuildings >= serverCity.getTOL().getRank())) {
|
(numDefenderBuildings >= serverCity.getTOL().getRank())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// passes validation: can assign auto-protection to war asset
|
// passes validation: can assign auto-protection to war asset
|
||||||
|
|
||||||
if (serverCity.getBane() != null)
|
|
||||||
if (serverCity.isLocationOnCityGrid(siegeBuilding.getBounds()))
|
|
||||||
if (player.getGuild().equals(serverCity.getBane().getOwner().getGuild()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (serverCity.getBane() != null)
|
||||||
|
if (serverCity.isLocationOnCityGrid(siegeBuilding.getBounds()))
|
||||||
|
if (player.getGuild().equals(serverCity.getBane().getOwner().getGuild()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
siegeBuilding.setProtectionState(ProtectionState.PROTECTED);
|
siegeBuilding.setProtectionState(ProtectionState.PROTECTED);
|
||||||
// No bane placed. We're done!
|
// No bane placed. We're done!
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,8 +506,8 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vector3fImmutable plantLoc = new Vector3fImmutable(treeInfo.getLoc().x,
|
Vector3fImmutable plantLoc = new Vector3fImmutable(treeInfo.getLoc().x,
|
||||||
serverZone.getHeightMap().getInterpolatedTerrainHeight(treeInfo.getLoc()),
|
serverZone.getHeightMap().getInterpolatedTerrainHeight(treeInfo.getLoc()),
|
||||||
treeInfo.getLoc().z);
|
treeInfo.getLoc().z);
|
||||||
|
|
||||||
cityObjects = DbManager.CityQueries.CREATE_CITY(playerCharacter.getObjectUUID(), serverZone.getObjectUUID(),
|
cityObjects = DbManager.CityQueries.CREATE_CITY(playerCharacter.getObjectUUID(), serverZone.getObjectUUID(),
|
||||||
serverRealm.getRealmID(),
|
serverRealm.getRealmID(),
|
||||||
@@ -527,18 +526,18 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
for (AbstractGameObject gameObject : cityObjects) {
|
for (AbstractGameObject gameObject : cityObjects) {
|
||||||
|
|
||||||
switch (gameObject.getObjectType()) {
|
switch (gameObject.getObjectType()) {
|
||||||
case Building:
|
case Building:
|
||||||
treeObject = (Building) gameObject;
|
treeObject = (Building) gameObject;
|
||||||
treeObject.runAfterLoad();
|
treeObject.runAfterLoad();
|
||||||
break;
|
break;
|
||||||
case City:
|
case City:
|
||||||
cityObject = (City) gameObject;
|
cityObject = (City) gameObject;
|
||||||
break;
|
break;
|
||||||
case Zone:
|
case Zone:
|
||||||
cityZone = (Zone) gameObject;
|
cityZone = (Zone) gameObject;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// log some error here? *** Refactor
|
// log some error here? *** Refactor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +561,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
ZoneManager.addZone(cityZone.getObjectUUID(), cityZone);
|
ZoneManager.addZone(cityZone.getObjectUUID(), cityZone);
|
||||||
ZoneManager.addPlayerCityZone(cityZone);
|
ZoneManager.addPlayerCityZone(cityZone);
|
||||||
serverZone.addNode(cityZone);
|
serverZone.addNode(cityZone);
|
||||||
|
|
||||||
cityZone.generateWorldAltitude();
|
cityZone.generateWorldAltitude();
|
||||||
|
|
||||||
cityObject.setParent(cityZone);
|
cityObject.setParent(cityZone);
|
||||||
@@ -708,7 +707,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blueprint = Blueprint.getBlueprint(msg.getFirstPlacementInfo().getBlueprintUUID());
|
blueprint = Blueprint.getBlueprint(msg.getFirstPlacementInfo().getBlueprintUUID());
|
||||||
|
|
||||||
if (blueprint == null){
|
if (blueprint == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -716,7 +715,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
for (Building building : serverZone.zoneBuildingSet) {
|
for (Building building : serverZone.zoneBuildingSet) {
|
||||||
if (building.getBlueprint() == null)
|
if (building.getBlueprint() == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE) {
|
if (building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE) {
|
||||||
if (building.getBlueprintUUID() == blueprint.getMeshForRank(0)) {
|
if (building.getBlueprintUUID() == blueprint.getMeshForRank(0)) {
|
||||||
PlaceAssetMsg.sendPlaceAssetError(origin, 43, ""); // "shrine of that type exists"
|
PlaceAssetMsg.sendPlaceAssetError(origin, 43, ""); // "shrine of that type exists"
|
||||||
@@ -811,13 +810,13 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
if (serverZone == null)
|
if (serverZone == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
if (player.getCharItemManager().getGoldTrading() > 0){
|
if (player.getCharItemManager().getGoldTrading() > 0){
|
||||||
ErrorPopupMsg.sendErrorPopup(player, 195);
|
ErrorPopupMsg.sendErrorPopup(player, 195);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Method checks validation conditions arising when placing
|
// Method checks validation conditions arising when placing
|
||||||
// buildings. Player must be on a city grid, must be
|
// buildings. Player must be on a city grid, must be
|
||||||
@@ -956,7 +955,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
// Method validates the location we have selected for our new city
|
// Method validates the location we have selected for our new city
|
||||||
|
|
||||||
private static boolean validateTreeOfLifePlacement(PlayerCharacter playerCharacter, Realm serverRealm, Zone serverZone,
|
private static boolean validateTreeOfLifePlacement(PlayerCharacter playerCharacter, Realm serverRealm, Zone serverZone,
|
||||||
ClientConnection origin, PlaceAssetMsg msg) {
|
ClientConnection origin, PlaceAssetMsg msg) {
|
||||||
|
|
||||||
PlacementInfo placementInfo = msg.getFirstPlacementInfo();
|
PlacementInfo placementInfo = msg.getFirstPlacementInfo();
|
||||||
|
|
||||||
@@ -995,7 +994,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
(realmType.equals(RealmType.MAELSTROM)) ||
|
(realmType.equals(RealmType.MAELSTROM)) ||
|
||||||
(realmType.equals(RealmType.OBLIVION))) {
|
(realmType.equals(RealmType.OBLIVION))) {
|
||||||
PlaceAssetMsg.sendPlaceAssetError(origin, 57, playerCharacter.getName()); // No building may be placed within this territory
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, playerCharacter.getName()); // No building may be placed within this territory
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1107,7 +1106,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
float buildingRotation = buildingInfo.getRot().y;
|
float buildingRotation = buildingInfo.getRot().y;
|
||||||
float vendorRotation = buildingInfo.getW();
|
float vendorRotation = buildingInfo.getW();
|
||||||
|
|
||||||
|
|
||||||
ArrayList<AbstractGameObject> shrineObjects = DbManager.ShrineQueries.CREATE_SHRINE(
|
ArrayList<AbstractGameObject> shrineObjects = DbManager.ShrineQueries.CREATE_SHRINE(
|
||||||
currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0),
|
currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0),
|
||||||
localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0,
|
localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0,
|
||||||
@@ -1121,20 +1120,20 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
for (AbstractGameObject ago : shrineObjects) {
|
for (AbstractGameObject ago : shrineObjects) {
|
||||||
|
|
||||||
switch (ago.getObjectType()) {
|
switch (ago.getObjectType()) {
|
||||||
case Building:
|
case Building:
|
||||||
newMesh = (Building) ago;
|
newMesh = (Building) ago;
|
||||||
newMesh.runAfterLoad();
|
newMesh.runAfterLoad();
|
||||||
newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING);
|
newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING);
|
||||||
MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7));
|
MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7));
|
||||||
WorldGrid.addObject(newMesh, player);
|
WorldGrid.addObject(newMesh, player);
|
||||||
break;
|
break;
|
||||||
case Shrine:
|
case Shrine:
|
||||||
newShrine = (Shrine) ago;
|
newShrine = (Shrine) ago;
|
||||||
newShrine.getShrineType().addShrineToServerList(newShrine);
|
newShrine.getShrineType().addShrineToServerList(newShrine);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
|
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1172,7 +1171,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
float vendorRotation = buildingInfo.getW();
|
float vendorRotation = buildingInfo.getW();
|
||||||
DateTime completionDate = DateTime.now().plusHours(blueprint.getRankTime(1));
|
DateTime completionDate = DateTime.now().plusHours(blueprint.getRankTime(1));
|
||||||
|
|
||||||
|
|
||||||
newMesh = DbManager.BuildingQueries.CREATE_BUILDING(
|
newMesh = DbManager.BuildingQueries.CREATE_BUILDING(
|
||||||
currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0),
|
currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0),
|
||||||
localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0,
|
localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0,
|
||||||
@@ -1249,6 +1248,85 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
RealmType currentRealm;
|
RealmType currentRealm;
|
||||||
|
|
||||||
|
if(Blueprint.getBlueprint(placementInfo.getBlueprintUUID()).isSiegeEquip() == false)
|
||||||
|
{
|
||||||
|
if (serverZone.isPlayerCity() == false) {
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
City city = ZoneManager.getCityAtLocation(placementInfo.getLoc());
|
||||||
|
|
||||||
|
if (player.getGuild().equals(city.getGuild()) == false) {
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (city.isLocationOnCityGrid(placementInfo.getLoc()) == false) {
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
City city = ZoneManager.getCityAtLocation(placementInfo.getLoc());
|
||||||
|
|
||||||
|
if(city == null)
|
||||||
|
{
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Bane bane = city.getBane();
|
||||||
|
//check if player is owner/IC of tree or bane
|
||||||
|
if (player.getGuild().equals(city.getGuild()) == true)
|
||||||
|
{
|
||||||
|
//is from owners guild
|
||||||
|
if(GuildStatusController.isGuildLeader(player.getGuildStatus()) == false && GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false)
|
||||||
|
{
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//is not from owners guild
|
||||||
|
if(bane == null)
|
||||||
|
{
|
||||||
|
//bane was null
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(city == null)
|
||||||
|
{
|
||||||
|
//city was null
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//check if player is from siege guild
|
||||||
|
if(player.getGuild().equals(bane.getOwner().getGuild()) == false)
|
||||||
|
{
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if player is GL or IC of the bane guild
|
||||||
|
if(GuildStatusController.isGuildLeader(player.getGuildStatus()) == false && GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false)
|
||||||
|
{
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cannot place on grid until bane is live
|
||||||
|
if(bane.getSiegePhase() != SiegePhase.WAR && city.isLocationOnCityGrid(placementInfo.getLoc()) == true)
|
||||||
|
{
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(city.isLocationWithinSiegeBounds(placementInfo.getLoc()) == false && city.isLocationOnCityZone(placementInfo.getLoc()) == false)
|
||||||
|
{
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Retrieve the building details we're placing
|
// Retrieve the building details we're placing
|
||||||
|
|
||||||
if (serverZone.isNPCCity() == true) {
|
if (serverZone.isNPCCity() == true) {
|
||||||
@@ -1259,7 +1337,14 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
// Errant guilds cannot place assets
|
// Errant guilds cannot place assets
|
||||||
|
|
||||||
if (player.getGuild().getGuildState() == GuildState.Errant) {
|
if (player.getGuild().getGuildState() == GuildState.Errant) {
|
||||||
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Only soverign or sworn guilds may place assets.");
|
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Only sovereign or sworn guilds may place assets.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player must be GL or IC of a guild to place buildings.
|
||||||
|
|
||||||
|
if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false && GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) {
|
||||||
|
PlaceAssetMsg.sendPlaceAssetError(origin, 10, ""); // You must be a guild leader
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1282,7 +1367,7 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
(currentRealm.equals(RealmType.MAELSTROM)) ||
|
(currentRealm.equals(RealmType.MAELSTROM)) ||
|
||||||
(currentRealm.equals(RealmType.OBLIVION))) {
|
(currentRealm.equals(RealmType.OBLIVION))) {
|
||||||
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName()); // No building may be placed within this territory
|
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName()); // No building may be placed within this territory
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1373,8 +1458,6 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
|
|||||||
PlaceAssetMsg.sendPlaceAssetError(origin, 9, ""); //You must be a guild member to place this asset
|
PlaceAssetMsg.sendPlaceAssetError(origin, 9, ""); //You must be a guild member to place this asset
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -999,11 +999,9 @@ public class City extends AbstractWorldObject {
|
|||||||
HashSet<Integer> currentMemory;
|
HashSet<Integer> currentMemory;
|
||||||
PlayerCharacter player;
|
PlayerCharacter player;
|
||||||
|
|
||||||
// Gather current list of players within a distance defined by the x extent of the
|
// Gather current list of players within a distance defined by the seige bounds
|
||||||
// city zone. As we want to grab all players with even a remote possibility of
|
|
||||||
// being in the zone, we might have to increase this distance.
|
|
||||||
|
|
||||||
currentPlayers = WorldGrid.getObjectsInRangePartial(this.location, this.parentZone.getBounds().getHalfExtents().x * 1.41421356237, MBServerStatics.MASK_PLAYER);
|
currentPlayers = WorldGrid.getObjectsInRangePartial(this.location, CityBoundsType.SIEGE.extents, MBServerStatics.MASK_PLAYER);
|
||||||
currentMemory = new HashSet<>();
|
currentMemory = new HashSet<>();
|
||||||
|
|
||||||
for (AbstractWorldObject playerObject : currentPlayers) {
|
for (AbstractWorldObject playerObject : currentPlayers) {
|
||||||
@@ -1019,9 +1017,8 @@ public class City extends AbstractWorldObject {
|
|||||||
if (_playerMemory.contains(player.getObjectUUID()))
|
if (_playerMemory.contains(player.getObjectUUID()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!this.isLocationOnCityZone(player.getLoc()))
|
if (!this.isLocationWithinSiegeBounds(player.getLoc()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Apply safehold affect to player if needed
|
// Apply safehold affect to player if needed
|
||||||
|
|
||||||
if ((this.isSafeHold == 1))
|
if ((this.isSafeHold == 1))
|
||||||
@@ -1065,7 +1062,8 @@ public class City extends AbstractWorldObject {
|
|||||||
|
|
||||||
|
|
||||||
player = PlayerCharacter.getFromCache(playerUUID);
|
player = PlayerCharacter.getFromCache(playerUUID);
|
||||||
if (this.isLocationOnCityZone(player.getLoc()))
|
|
||||||
|
if (this.isLocationWithinSiegeBounds(player.getLoc()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Remove players safezone status if warranted
|
// Remove players safezone status if warranted
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class Mob extends AbstractIntelligenceAgent {
|
|||||||
public boolean despawned = false;
|
public boolean despawned = false;
|
||||||
public Vector3fImmutable destination = Vector3fImmutable.ZERO;
|
public Vector3fImmutable destination = Vector3fImmutable.ZERO;
|
||||||
public Vector3fImmutable localLoc = Vector3fImmutable.ZERO;
|
public Vector3fImmutable localLoc = Vector3fImmutable.ZERO;
|
||||||
|
public HashMap<Integer,Integer> mobPowers;
|
||||||
/**
|
/**
|
||||||
* No Id Constructor
|
* No Id Constructor
|
||||||
*/
|
*/
|
||||||
@@ -365,6 +365,7 @@ public class Mob extends AbstractIntelligenceAgent {
|
|||||||
this.setObjectTypeMask(MBServerStatics.MASK_PET | this.getTypeMasks());
|
this.setObjectTypeMask(MBServerStatics.MASK_PET | this.getTypeMasks());
|
||||||
if (ConfigManager.serverType.equals(ServerType.LOGINSERVER))
|
if (ConfigManager.serverType.equals(ServerType.LOGINSERVER))
|
||||||
this.setLoc(this.getLoc());
|
this.setLoc(this.getLoc());
|
||||||
|
mobPowers = DbManager.MobBaseQueries.LOAD_STATIC_POWERS(this.getMobBaseID());
|
||||||
}
|
}
|
||||||
if (!isPet && this.contract == null) {
|
if (!isPet && this.contract == null) {
|
||||||
this.level = (short) this.mobBase.getLevel();
|
this.level = (short) this.mobBase.getLevel();
|
||||||
@@ -393,10 +394,10 @@ public class Mob extends AbstractIntelligenceAgent {
|
|||||||
//TODO set these correctly later
|
//TODO set these correctly later
|
||||||
this.rangeHandOne = 8;
|
this.rangeHandOne = 8;
|
||||||
this.rangeHandTwo = -1;
|
this.rangeHandTwo = -1;
|
||||||
this.minDamageHandOne = 0;
|
this.minDamageHandOne = 0;
|
||||||
this.maxDamageHandOne = 0;
|
this.maxDamageHandOne = 0;
|
||||||
this.minDamageHandTwo = 1;
|
this.minDamageHandTwo = 1;
|
||||||
this.maxDamageHandTwo = 4;
|
this.maxDamageHandTwo = 4;
|
||||||
this.atrHandOne = 300;
|
this.atrHandOne = 300;
|
||||||
this.atrHandOne = 300;
|
this.atrHandOne = 300;
|
||||||
this.defenseRating = (short) this.mobBase.getDefenseRating();
|
this.defenseRating = (short) this.mobBase.getDefenseRating();
|
||||||
@@ -2201,7 +2202,7 @@ public class Mob extends AbstractIntelligenceAgent {
|
|||||||
} catch(Exception e){
|
} catch(Exception e){
|
||||||
Logger.error( e.getMessage());
|
Logger.error( e.getMessage());
|
||||||
}
|
}
|
||||||
|
mobPowers = DbManager.MobBaseQueries.LOAD_STATIC_POWERS(this.getMobBaseID());
|
||||||
if (this.equip == null) {
|
if (this.equip == null) {
|
||||||
Logger.error("Null equipset returned for uuid " + currentID);
|
Logger.error("Null equipset returned for uuid " + currentID);
|
||||||
this.equip = new HashMap<>(0);
|
this.equip = new HashMap<>(0);
|
||||||
|
|||||||
@@ -4781,8 +4781,11 @@ public void dismissNecroPets() {
|
|||||||
}finally{
|
}finally{
|
||||||
this.updateLock.writeLock().unlock();
|
this.updateLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
//temp removal
|
||||||
|
if(AbstractCharacter.CanFly(this) == false && this.altitude > 0)
|
||||||
|
GroundPlayer(this);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void updateFlight() {
|
public void updateFlight() {
|
||||||
|
|||||||
@@ -76,75 +76,108 @@ public abstract class AbstractPowerAction {
|
|||||||
int token = DbManager.hasher.SBStringHash(IDString);
|
int token = DbManager.hasher.SBStringHash(IDString);
|
||||||
//cache token, used for applying effects.
|
//cache token, used for applying effects.
|
||||||
PowersManager.ActionTokenByIDString.put(IDString, token);
|
PowersManager.ActionTokenByIDString.put(IDString, token);
|
||||||
if (type.equals("ApplyEffect"))
|
apa = null;
|
||||||
apa = new ApplyEffectPowerAction(rs, effects);
|
switch (type)
|
||||||
else if (type.equals("ApplyEffects"))
|
{
|
||||||
apa = new ApplyEffectsPowerAction(rs, effects);
|
default:
|
||||||
else if (type.equals("DeferredPower"))
|
Logger.error("valid type not found for poweraction of ID" + rs.getInt("ID"));
|
||||||
apa = new DeferredPowerPowerAction(rs, effects);
|
break;
|
||||||
else if (type.equals("DamageOverTime"))
|
case "ApplyEffect":
|
||||||
apa = new DamageOverTimePowerAction(rs, effects);
|
apa = new ApplyEffectPowerAction(rs, effects);
|
||||||
else if (type.equals("Peek"))
|
break;
|
||||||
apa = new PeekPowerAction(rs);
|
case "ApplyEffects":
|
||||||
else if (type.equals("Charm"))
|
apa = new ApplyEffectsPowerAction(rs, effects);
|
||||||
apa = new CharmPowerAction(rs);
|
break;
|
||||||
else if (type.equals("Fear"))
|
case "DeferredPower":
|
||||||
apa = new FearPowerAction(rs);
|
apa = new DeferredPowerPowerAction(rs, effects);
|
||||||
else if (type.equals("Confusion"))
|
break;
|
||||||
apa = new ConfusionPowerAction(rs);
|
case "DamageOverTime":
|
||||||
else if (type.equals("RemoveEffect"))
|
apa = new DamageOverTimePowerAction(rs, effects);
|
||||||
apa = new RemoveEffectPowerAction(rs);
|
break;
|
||||||
else if (type.equals("Track"))
|
case "Peek":
|
||||||
apa = new TrackPowerAction(rs, effects);
|
apa = new PeekPowerAction(rs);
|
||||||
else if (type.equals("DirectDamage"))
|
break;
|
||||||
apa = new DirectDamagePowerAction(rs, effects);
|
case "Charm":
|
||||||
else if (type.equals("Transform"))
|
apa = new CharmPowerAction(rs);
|
||||||
apa = new TransformPowerAction(rs, effects);
|
break;
|
||||||
else if (type.equals("CreateMob"))
|
case "Fear":
|
||||||
apa = new CreateMobPowerAction(rs);
|
apa = new FearPowerAction(rs);
|
||||||
else if (type.equals("Invis"))
|
break;
|
||||||
apa = new InvisPowerAction(rs, effects);
|
case "Confusion":
|
||||||
else if (type.equals("ClearNearbyAggro"))
|
apa = new ConfusionPowerAction(rs);
|
||||||
apa = new ClearNearbyAggroPowerAction(rs);
|
break;
|
||||||
else if (type.equals("MobRecall"))
|
case "RemoveEffect":
|
||||||
apa = new MobRecallPowerAction(rs);
|
apa = new RemoveEffectPowerAction(rs);
|
||||||
else if (type.equals("SetItemFlag"))
|
break;
|
||||||
apa = new SetItemFlagPowerAction(rs);
|
case "Track":
|
||||||
else if (type.equals("SimpleDamage"))
|
apa = new TrackPowerAction(rs, effects);
|
||||||
apa = new SimpleDamagePowerAction(rs);
|
break;
|
||||||
else if (type.equals("TransferStatOT"))
|
case "DirectDamage":
|
||||||
apa = new TransferStatOTPowerAction(rs, effects);
|
apa = new DirectDamagePowerAction(rs, effects);
|
||||||
else if (type.equals("TransferStat"))
|
break;
|
||||||
apa = new TransferStatPowerAction(rs, effects);
|
case "Transform":
|
||||||
else if (type.equals("Teleport"))
|
apa = new TransformPowerAction(rs, effects);
|
||||||
apa = new TeleportPowerAction(rs);
|
break;
|
||||||
else if (type.equals("TreeChoke"))
|
case "CreateMob":
|
||||||
apa = new TreeChokePowerAction(rs);
|
apa = new CreateMobPowerAction(rs);
|
||||||
else if (type.equals("Block"))
|
break;
|
||||||
apa = new BlockPowerAction(rs);
|
case "Invis":
|
||||||
else if (type.equals("Resurrect"))
|
apa = new InvisPowerAction(rs, effects);
|
||||||
apa = new ResurrectPowerAction(rs);
|
break;
|
||||||
else if (type.equals("ClearAggro"))
|
case "ClearNearbyAggro":
|
||||||
apa = new ClearAggroPowerAction(rs);
|
apa = new ClearNearbyAggroPowerAction(rs);
|
||||||
else if (type.equals("ClaimMine"))
|
break;
|
||||||
apa = new ClaimMinePowerAction(rs);
|
case "MobRecall":
|
||||||
else if (type.equals("Recall"))
|
apa = new MobRecallPowerAction(rs);
|
||||||
apa = new RecallPowerAction(rs);
|
break;
|
||||||
else if (type.equals("SpireDisable"))
|
case "SetItemFlag":
|
||||||
apa = new SpireDisablePowerAction(rs);
|
apa = new SetItemFlagPowerAction(rs);
|
||||||
else if (type.equals("Steal"))
|
break;
|
||||||
apa = new StealPowerAction(rs);
|
case "SimpleDamage":
|
||||||
else if (type.equals("Summon"))
|
apa = new SimpleDamagePowerAction(rs);
|
||||||
apa = new SummonPowerAction(rs);
|
break;
|
||||||
else if (type.equals("RunegateTeleport"))
|
case "TransferStatOT":
|
||||||
apa = new RunegateTeleportPowerAction(rs);
|
apa = new TransferStatOTPowerAction(rs, effects);
|
||||||
else if (type.equals("RunegateTeleport"))
|
break;
|
||||||
apa = new RunegateTeleportPowerAction(rs);
|
case "TransferStat":
|
||||||
else if (type.equals("OpenGate"))
|
apa = new TransferStatPowerAction(rs, effects);
|
||||||
apa = new OpenGatePowerAction(rs);
|
break;
|
||||||
else {
|
case "Teleport":
|
||||||
Logger.error("valid type not found for poweraction of ID" + rs.getInt("ID"));
|
apa = new TeleportPowerAction(rs);
|
||||||
continue;
|
break;
|
||||||
|
case "TreeChoke":
|
||||||
|
apa = new TreeChokePowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "Block":
|
||||||
|
apa = new BlockPowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "Resurrect":
|
||||||
|
apa = new ResurrectPowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "ClearAggro":
|
||||||
|
apa = new ClearAggroPowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "ClaimMine":
|
||||||
|
apa = new ClaimMinePowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "Recall":
|
||||||
|
apa = new RecallPowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "SpireDisable":
|
||||||
|
apa = new SpireDisablePowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "Steal":
|
||||||
|
apa = new StealPowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "Summon":
|
||||||
|
apa = new SummonPowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "RunegateTeleport":
|
||||||
|
apa = new RunegateTeleportPowerAction(rs);
|
||||||
|
break;
|
||||||
|
case "OpenGate":
|
||||||
|
apa = new OpenGatePowerAction(rs);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
powerActions.put(IDString, apa);
|
powerActions.put(IDString, apa);
|
||||||
powerActionsByID.put(apa.UUID, apa);
|
powerActionsByID.put(apa.UUID, apa);
|
||||||
|
|||||||
@@ -124,16 +124,6 @@ public class MBServerStatics {
|
|||||||
public static final int CHM_THREAD_MED = 2;
|
public static final int CHM_THREAD_MED = 2;
|
||||||
public static final int CHM_THREAD_LOW = 1;
|
public static final int CHM_THREAD_LOW = 1;
|
||||||
|
|
||||||
/*
|
|
||||||
* LoginServer related
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static final String PCMajorVer = "1.2.25.5";
|
|
||||||
public static final String PCMinorVer = "5.25.5";
|
|
||||||
|
|
||||||
public static final String MACMajorVer = "1.2.24.3";
|
|
||||||
public static final String MACMinorVer = "5.24.3";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LoginErrorMsg related
|
* LoginErrorMsg related
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -169,8 +169,8 @@ public class LoginServer {
|
|||||||
|
|
||||||
// Configure the VersionInfoMsgs:
|
// Configure the VersionInfoMsgs:
|
||||||
|
|
||||||
this.versionInfoMessage = new VersionInfoMsg(MBServerStatics.PCMajorVer,
|
this.versionInfoMessage = new VersionInfoMsg(ConfigManager.MB_MAJOR_VER.getValue(),
|
||||||
MBServerStatics.PCMinorVer);
|
ConfigManager.MB_MINOR_VER.getValue());
|
||||||
|
|
||||||
Logger.info("Initializing Database Pool");
|
Logger.info("Initializing Database Pool");
|
||||||
initDatabasePool();
|
initDatabasePool();
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ public class LoginServerMsgHandler implements NetMsgHandler {
|
|||||||
|
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
|
|
||||||
if (ConfigManager.MB_LOGIN_AUTOREG.getValue().equals("FALSE")) {
|
if (ConfigManager.MB_LOGIN_AUTOREG.getValue().equalsIgnoreCase("false")) {
|
||||||
this.KickToLogin(MBServerStatics.LOGINERROR_INVALID_USERNAME_PASSWORD, "Could not find account (" + uname + ')', clientConnection);
|
this.KickToLogin(MBServerStatics.LOGINERROR_INVALID_USERNAME_PASSWORD, "Could not find account (" + uname + ')', clientConnection);
|
||||||
Logger.info("Could not find account (" + uname + ')');
|
Logger.info("Could not find account (" + uname + ')');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ import org.pmw.tinylog.labelers.TimestampLabeler;
|
|||||||
import org.pmw.tinylog.policies.StartupPolicy;
|
import org.pmw.tinylog.policies.StartupPolicy;
|
||||||
import org.pmw.tinylog.writers.RollingFileWriter;
|
import org.pmw.tinylog.writers.RollingFileWriter;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|||||||
@@ -9,27 +9,19 @@
|
|||||||
|
|
||||||
package engine.util;
|
package engine.util;
|
||||||
|
|
||||||
|
import engine.gameManager.ConfigManager;
|
||||||
import engine.server.MBServerStatics;
|
import engine.server.MBServerStatics;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
|
|
||||||
public class MiscUtils {
|
public class MiscUtils {
|
||||||
|
|
||||||
// no need to recompile these each call, put them in object scope and
|
|
||||||
// compile just once.
|
|
||||||
private static final Pattern lastNameRegex = Pattern
|
|
||||||
.compile("^[A-Za-z][-'A-Za-z\\x20]*$");
|
|
||||||
private static final Pattern firstNameRegex = Pattern
|
|
||||||
.compile("^[A-Za-z]+$");
|
|
||||||
|
|
||||||
public static boolean checkIfFirstNameInvalid(String firstName) {
|
public static boolean checkIfFirstNameInvalid(String firstName) {
|
||||||
if ((firstName == null) || (firstName.length() == 0)
|
if ((firstName == null) || (firstName.length() == 0)
|
||||||
|| (firstName.length() > MBServerStatics.MAX_NAME_LENGTH)
|
|| (firstName.length() > MBServerStatics.MAX_NAME_LENGTH)
|
||||||
|| (firstName.length() < MBServerStatics.MIN_NAME_LENGTH)) {
|
|| (firstName.length() < MBServerStatics.MIN_NAME_LENGTH)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return (!firstNameRegex.matcher(firstName).matches());
|
return (!ConfigManager.regex.get(ConfigManager.MB_LOGIN_FNAME_REGEX).matcher(firstName).matches());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean checkIfLastNameInvalid(String lastName) {
|
public static boolean checkIfLastNameInvalid(String lastName) {
|
||||||
@@ -43,52 +35,4 @@ public class MiscUtils {
|
|||||||
// empty last names are fine, return false
|
// empty last names are fine, return false
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getCallingMethodName() {
|
|
||||||
StackTraceElement e[] = Thread.currentThread().getStackTrace();
|
|
||||||
int numElements = e.length;
|
|
||||||
|
|
||||||
if (numElements < 1) {
|
|
||||||
return "NoStack";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numElements == 1) {
|
|
||||||
return e[0].getMethodName();
|
|
||||||
} else if (numElements == 2) {
|
|
||||||
return e[1].getMethodName();
|
|
||||||
} else if (numElements == 3) {
|
|
||||||
return e[2].getMethodName();
|
|
||||||
} else {
|
|
||||||
return e[3].getMethodName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCallStackAsString() {
|
|
||||||
String out = "";
|
|
||||||
|
|
||||||
StackTraceElement e[] = Thread.currentThread().getStackTrace();
|
|
||||||
int numElements = e.length;
|
|
||||||
|
|
||||||
for (int i = (numElements - 1); i > 1; --i) {
|
|
||||||
|
|
||||||
String[] classStack = e[i].getClassName().split("\\.");
|
|
||||||
String methName = e[i].getMethodName();
|
|
||||||
|
|
||||||
String className = classStack[classStack.length - 1];
|
|
||||||
|
|
||||||
if (methName.equals("<init>")) {
|
|
||||||
methName = className;
|
|
||||||
}
|
|
||||||
|
|
||||||
out += className + '.' + methName + "()";
|
|
||||||
|
|
||||||
if (i > 2) {
|
|
||||||
out += " -> ";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user