2022-04-30 09:41:17 -04:00
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.objects ;
import engine.Enum ;
import engine.Enum.ProtectionState ;
import engine.Enum.SiegePhase ;
import engine.Enum.SiegeResult ;
import engine.InterestManagement.WorldGrid ;
import engine.db.archive.BaneRecord ;
import engine.db.archive.DataWarehouse ;
import engine.gameManager.BuildingManager ;
import engine.gameManager.ChatManager ;
import engine.gameManager.DbManager ;
import engine.gameManager.ZoneManager ;
import engine.job.JobScheduler ;
import engine.jobs.ActivateBaneJob ;
import engine.jobs.BaneDefaultTimeJob ;
import engine.math.Vector3fImmutable ;
import engine.net.DispatchMessage ;
import engine.net.client.ClientConnection ;
import engine.net.client.msg.PlaceAssetMsg ;
import engine.net.client.msg.chat.ChatSystemMsg ;
import engine.server.MBServerStatics ;
import org.joda.time.DateTime ;
import org.pmw.tinylog.Logger ;
import java.sql.ResultSet ;
import java.sql.SQLException ;
import java.util.ArrayList ;
import java.util.Date ;
import java.util.concurrent.ConcurrentHashMap ;
public final class Bane {
2023-07-15 09:23:48 -04:00
public static ConcurrentHashMap < Integer , Bane > banes = new ConcurrentHashMap <> ( MBServerStatics . CHM_INIT_CAP , MBServerStatics . CHM_LOAD , MBServerStatics . CHM_THREAD_LOW );
2022-04-30 09:41:17 -04:00
private final int cityUUID ;
private final int stoneUUID ;
2023-07-15 09:23:48 -04:00
private int ownerUUID ;
2022-04-30 09:41:17 -04:00
private DateTime placementDate = null ;
private DateTime liveDate = null ;
private BaneDefaultTimeJob defaultTimeJob ;
// Internal cache for banes
2023-07-15 09:23:48 -04:00
private ActivateBaneJob activateBaneJob ;
2022-04-30 09:41:17 -04:00
/**
* ResultSet Constructor
*/
public Bane ( ResultSet rs ) throws SQLException {
Date sqlDateTime ;
ActivateBaneJob abtj ;
this . cityUUID = rs . getInt ( "cityUUID" );
this . ownerUUID = rs . getInt ( "ownerUUID" );
this . stoneUUID = rs . getInt ( "stoneUUID" );
sqlDateTime = rs . getTimestamp ( "placementDate" );
if ( sqlDateTime != null )
this . placementDate = new DateTime ( sqlDateTime );
sqlDateTime = rs . getTimestamp ( "liveDate" );
if ( sqlDateTime != null )
this . liveDate = new DateTime ( sqlDateTime );
switch ( this . getSiegePhase ()) {
case CHALLENGE :
break ;
case WAR :
// cancel old job if exists
if ( this . activateBaneJob != null )
this . activateBaneJob . cancelJob ();
abtj = new ActivateBaneJob ( cityUUID );
JobScheduler . getInstance (). scheduleJob ( abtj , this . liveDate . getMillis ());
this . activateBaneJob = abtj ;
break ;
case STANDOFF :
// cancel old job if exists
if ( this . activateBaneJob != null )
this . activateBaneJob . cancelJob ();
abtj = new ActivateBaneJob ( cityUUID );
JobScheduler . getInstance (). scheduleJob ( abtj , this . liveDate . getMillis ());
this . activateBaneJob = abtj ;
break ;
}
if ( this . liveDate == null )
setDefaultTime ();
}
public static boolean summonBanestone ( PlayerCharacter player , ClientConnection origin , int rank ) {
Guild baningGuild ;
Zone cityZone ;
City targetCity ;
ArrayList < Bane > nationBanes ;
baningGuild = player . getGuild ();
2023-01-21 09:28:30 -05:00
if ( baningGuild . getNation (). isEmptyGuild ()) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 55 , "" ); // You must be in a Nation
2022-04-30 09:41:17 -04:00
return false ;
}
if ( baningGuild . getNation (). isNPCGuild ()) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 72 , "" ); // Cannot be in an NPC nation
2022-04-30 09:41:17 -04:00
return false ;
}
if ( GuildStatusController . isInnerCouncil ( player . getGuildStatus ()) == false ) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 10 , "" ); // You must be a guild leader
2022-04-30 09:41:17 -04:00
return false ;
}
2023-05-07 10:20:53 -04:00
// Cannot place banestone underwater;
2022-04-30 09:41:17 -04:00
2023-09-20 14:31:48 -04:00
if ( ZoneManager . isLocUnderwater ( player . getLoc ())) {
2022-04-30 09:41:17 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 6 , "" ); // Cannot place underwater
return false ;
}
2023-05-07 10:20:53 -04:00
// figure out which city zone we are standing on.
2022-04-30 09:41:17 -04:00
targetCity = ZoneManager . getCityAtLocation ( player . getLoc ());
if ( targetCity == null ) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 59 , "" ); // No city to siege at this location!
2022-04-30 09:41:17 -04:00
return false ;
}
if ( targetCity . isSafeHold ()) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 15 , "" ); // Cannot place assets in peace zone
2022-04-30 09:41:17 -04:00
return false ;
}
if ( targetCity . getRank () > rank ) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 60 , "" ); // Bane rank is too low
2022-04-30 09:41:17 -04:00
return false ;
}
cityZone = targetCity . getParent ();
// Cannot place assets on a dead tree
2023-09-20 16:05:57 -04:00
if (( cityZone . guild_zone ) &&
2023-09-20 15:53:41 -04:00
( City . getCity ( cityZone . playerCityUUID ). getTOL (). getRank () == - 1 )) {
2022-04-30 09:41:17 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 1 , "Cannot bane a dead tree!" );
return false ;
}
if ( baningGuild . getNation () == targetCity . getGuild (). getNation ()) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 20 , "" ); //Cannot bane yourself!
2022-04-30 09:41:17 -04:00
return false ;
}
if ( targetCity . getTOL () == null ) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 65 , "" ); // Cannot find tree to target
2022-04-30 09:41:17 -04:00
return false ;
}
if ( targetCity . getBane () != null ) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 23 , "" ); // Tree is already baned.
2022-04-30 09:41:17 -04:00
return false ;
}
if ( getBaneByAttackerGuild ( baningGuild ) != null ) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 1 , "Your guild has already placed a bane!" );
2022-04-30 09:41:17 -04:00
return false ;
}
nationBanes = getBanesByNation ( baningGuild . getNation ());
// A nation can only have 3 concurrent banes
if ( nationBanes . size () == 3 ) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 64 , "" ); // Your nation is already at war and your limit has been reached
2022-04-30 09:41:17 -04:00
return false ;
}
if ( targetCity . isLocationOnCityGrid ( player . getLoc ()) == true ) {
2023-07-15 09:23:48 -04:00
PlaceAssetMsg . sendPlaceAssetError ( origin , 1 , "Cannot place banestone on city grid." );
2022-04-30 09:41:17 -04:00
return false ;
}
Blueprint blueprint = Blueprint . getBlueprint ( 24300 ); // Banestone
//Let's drop a banestone!
Vector3fImmutable localLocation = ZoneManager . worldToLocal ( player . getLoc (), cityZone );
if ( localLocation == null ) {
PlaceAssetMsg . sendPlaceAssetError ( player . getClientConnection (), 1 , "A Serious error has occurred. Please post details for to ensure transaction integrity" );
Logger . info ( "Failed to Convert World coordinates to local zone coordinates" );
return false ;
}
Building stone = DbManager . BuildingQueries . CREATE_BUILDING (
cityZone . getObjectUUID (), player . getObjectUUID (), blueprint . getName (), blueprint . getBlueprintUUID (),
localLocation , 1 . 0f , blueprint . getMaxHealth ( rank ), ProtectionState . PROTECTED , 0 , rank ,
null , blueprint . getBlueprintUUID (), 1 , 0 . 0f );
if ( stone == null ) {
PlaceAssetMsg . sendPlaceAssetError ( player . getClientConnection (), 1 , "A Serious error has occurred. Please post details for to ensure transaction integrity" );
return false ;
}
stone . addEffectBit (( 1 << 19 ));
stone . setMaxHitPoints ( stone . getBlueprint (). getMaxHealth ( stone . getRank ()));
stone . setCurrentHitPoints ( stone . getMaxHitPoints ());
BuildingManager . setUpgradeDateTime ( stone , null , 0 );
//Make the bane
Bane bane = makeBane ( player , targetCity , stone );
if ( bane == null ) {
//delete bane stone, failed to make bane object
DbManager . BuildingQueries . DELETE_FROM_DATABASE ( stone );
PlaceAssetMsg . sendPlaceAssetError ( player . getClientConnection (), 1 , "A Serious error has occurred. Please post details for to ensure transaction integrity" );
return false ;
}
WorldGrid . addObject ( stone , player );
//Add bane effect to TOL
targetCity . getTOL (). addEffectBit (( 1 << 16 ));
targetCity . getTOL (). updateEffects ();
2023-07-15 09:23:48 -04:00
2022-04-30 09:41:17 -04:00
Vector3fImmutable movePlayerOutsideStone = player . getLoc ();
movePlayerOutsideStone = movePlayerOutsideStone . setX ( movePlayerOutsideStone . x + 10 );
movePlayerOutsideStone = movePlayerOutsideStone . setZ ( movePlayerOutsideStone . z + 10 );
player . teleport ( movePlayerOutsideStone );
// Notify players
ChatSystemMsg chatMsg = new ChatSystemMsg ( null , "[Bane Channel] " + targetCity . getCityName () + " has been baned by " + baningGuild . getName () + ". Standoff phase has begun!" );
chatMsg . setMessageType ( 4 );
chatMsg . setChannel ( Enum . ChatChannelType . SYSTEM . getChannelID ());
DispatchMessage . dispatchMsgToAll ( chatMsg );
// Push this event to the DataWarehouse
BaneRecord baneRecord = BaneRecord . borrow ( bane , Enum . RecordEventType . PENDING );
DataWarehouse . pushToWarehouse ( baneRecord );
return true ;
}
2023-07-15 09:23:48 -04:00
public static Bane getBane ( int cityUUID ) {
Bane outBane ;
// Check cache first
outBane = banes . get ( cityUUID );
// Last resort attempt to load from database
if ( outBane == null )
outBane = DbManager . BaneQueries . LOAD_BANE ( cityUUID );
else
return outBane ;
// As we loaded from the db, store it in the internal cache
if ( outBane != null )
banes . put ( cityUUID , outBane );
return outBane ;
}
public static Bane getBaneByAttackerGuild ( Guild guild ) {
if ( guild == null || guild . isEmptyGuild ())
return null ;
ArrayList < Bane > baneList ;
baneList = new ArrayList <> ( banes . values ());
for ( Bane bane : baneList ) {
if ( bane . getOwner (). getGuild (). equals ( guild ))
return bane ;
}
return null ;
}
public static ArrayList < Bane > getBanesByNation ( Guild guild ) {
ArrayList < Bane > baneList ;
ArrayList < Bane > returnList ;
baneList = new ArrayList <> ( banes . values ());
returnList = new ArrayList <> ();
for ( Bane bane : baneList ) {
if ( bane . getOwner (). getGuild (). getNation (). equals ( guild ))
returnList . add ( bane );
}
return returnList ;
}
public static void addBane ( Bane bane ) {
Bane . banes . put ( bane . cityUUID , bane );
}
public static Bane makeBane ( PlayerCharacter owner , City city , Building stone ) {
Bane newBane ;
if ( DbManager . BaneQueries . CREATE_BANE ( city , owner , stone ) == false ) {
Logger . error ( "Error writing to database" );
return null ;
}
newBane = DbManager . BaneQueries . LOAD_BANE ( city . getObjectUUID ());
return newBane ;
}
//Call this to prematurely end a bane
2022-04-30 09:41:17 -04:00
public SiegePhase getSiegePhase () {
SiegePhase phase ;
if ( this . liveDate == null ) {
phase = SiegePhase . CHALLENGE ; //challenge
return phase ;
}
if ( DateTime . now (). isAfter ( this . liveDate )) {
2023-07-15 09:23:48 -04:00
phase = SiegePhase . WAR ; //war
2022-04-30 09:41:17 -04:00
return phase ;
}
// If code reaches this point we are in standoff mode.
2023-07-15 09:23:48 -04:00
phase = SiegePhase . STANDOFF ; //standoff
2022-04-30 09:41:17 -04:00
return phase ;
}
2023-07-15 09:23:48 -04:00
// Cache access
2022-04-30 09:41:17 -04:00
private void setDefaultTime () {
DateTime timeToSetDefault = new DateTime ( this . placementDate );
timeToSetDefault = timeToSetDefault . plusDays ( 1 );
DateTime currentTime = DateTime . now ();
DateTime defaultTime = new DateTime ( this . placementDate );
defaultTime = defaultTime . plusDays ( 2 );
defaultTime = defaultTime . hourOfDay (). setCopy ( 22 );
defaultTime = defaultTime . minuteOfHour (). setCopy ( 0 );
defaultTime = defaultTime . secondOfMinute (). setCopy ( 0 );
if ( currentTime . isAfter ( timeToSetDefault ))
this . setLiveDate ( defaultTime );
else {
if ( this . defaultTimeJob != null )
this . defaultTimeJob . cancelJob ();
BaneDefaultTimeJob bdtj = new BaneDefaultTimeJob ( this );
JobScheduler . getInstance (). scheduleJob ( bdtj , timeToSetDefault . getMillis ());
this . defaultTimeJob = bdtj ;
}
}
2023-07-15 09:23:48 -04:00
//Returns a guild's bane
/*
* Getters
*/
public City getCity () {
return City . getCity ( this . cityUUID );
}
public PlayerCharacter getOwner () {
return ( PlayerCharacter ) DbManager . getObject ( Enum . GameObjectType . PlayerCharacter , ownerUUID );
}
public boolean remove () {
Building baneStone ;
baneStone = this . getStone ();
if ( baneStone == null ) {
Logger . debug ( "Removing bane without a stone." );
return false ;
}
// Reassert protection contracts
this . getCity (). protectionEnforced = true ;
// Remove visual effects on Bane and TOL.
this . getStone (). removeAllVisualEffects ();
this . getStone (). updateEffects ();
this . getCity (). getTOL (). removeAllVisualEffects ();
this . getCity (). getTOL (). updateEffects ();
// Remove bane from the database
if ( DbManager . BaneQueries . REMOVE_BANE ( this ) == false ) {
Logger . error ( "Database call failed for city UUID: " + this . getCity (). getObjectUUID ());
return false ;
}
// Remove bane from ingame cache
Bane . banes . remove ( cityUUID );
// Delete stone from database
if ( DbManager . BuildingQueries . DELETE_FROM_DATABASE ( baneStone ) == false ) {
Logger . error ( "Database error when deleting stone object" );
return false ;
}
// Remove object from simulation
baneStone . removeFromCache ();
WorldGrid . RemoveWorldObject ( baneStone );
WorldGrid . removeObject ( baneStone );
return true ;
}
public final DateTime getPlacementDate () {
return placementDate ;
}
public final boolean isAccepted () {
return ( this . getSiegePhase () != SiegePhase . CHALLENGE );
}
public final DateTime getLiveDate () {
return liveDate ;
}
2022-04-30 09:41:17 -04:00
public void setLiveDate ( DateTime baneTime ) {
if ( DbManager . BaneQueries . SET_BANE_TIME ( baneTime , this . getCity (). getObjectUUID ())) {
this . liveDate = new DateTime ( baneTime );
// Push event to warehouse
BaneRecord . updateLiveDate ( this , baneTime );
ChatManager . chatGuildInfo ( this . getOwner (). getGuild (), "The bane on " + this . getCity (). getGuild (). getName () + " has been set! Standoff phase has begun!" );
Guild attackerNation = this . getOwner (). getGuild (). getNation ();
if ( attackerNation != null )
for ( Guild subGuild : attackerNation . getSubGuildList ()) {
//Don't send the message twice.
if ( subGuild . equals ( attackerNation ))
continue ;
ChatManager . chatGuildInfo ( subGuild , "The bane on " + this . getCity (). getGuild (). getName () + " has been set! Standoff phase has begun!" );
}
ChatManager . chatGuildInfo ( this . getCity (). getGuild (), "The bane on " + this . getCity (). getGuild (). getName () + " has been set! Standoff phase has begun!" );
Guild defenderNation = this . getCity (). getGuild (). getNation ();
if ( defenderNation != null ) {
if ( defenderNation != this . getCity (). getGuild ())
ChatManager . chatGuildInfo ( defenderNation , "The bane on " + this . getCity (). getGuild (). getName () + " has been set! Standoff phase has begun!" );
for ( Guild subGuild : defenderNation . getSubGuildList ()) {
//Don't send the message twice.
if ( subGuild == this . getCity (). getGuild ())
continue ;
ChatManager . chatGuildInfo ( subGuild , "The bane on " + this . getCity (). getGuild (). getName () + " has been set! Standoff phase has begun!" );
}
}
if ( activateBaneJob != null )
this . activateBaneJob . cancelJob ();
ActivateBaneJob abtj = new ActivateBaneJob ( cityUUID );
JobScheduler . getInstance (). scheduleJob ( abtj , this . liveDate . getMillis ());
this . activateBaneJob = abtj ;
} else {
2023-07-15 09:23:48 -04:00
Logger . debug ( "error with city " + this . getCity (). getName ());
2022-04-30 09:41:17 -04:00
ChatManager . chatGuildInfo ( this . getOwner (). getGuild (), "A Serious error has occurred. Please post details for to ensure transaction integrity" );
ChatManager . chatGuildInfo ( this . getCity (). getGuild (), "A Serious error has occurred. Please post details for to ensure transaction integrity" );
}
}
public final void endBane ( SiegeResult siegeResult ) {
boolean baneRemoved ;
// No matter what the outcome of a bane, we re-asset
// protection contracts at this time. They don't quite
// matter if the city falls, as they are invalidated.
2023-07-15 09:23:48 -04:00
2022-04-30 09:41:17 -04:00
this . getCity (). protectionEnforced = true ;
2023-07-15 09:23:48 -04:00
2022-04-30 09:41:17 -04:00
switch ( siegeResult ) {
case DEFEND :
// Push event to warehouse
BaneRecord . updateResolution ( this , Enum . RecordEventType . DEFEND );
baneRemoved = remove ();
if ( baneRemoved ) {
// Update seieges withstood
2023-07-15 09:23:48 -04:00
2022-04-30 09:41:17 -04:00
this . getCity (). setSiegesWithstood ( this . getCity (). getSiegesWithstood () + 1 );
2023-07-15 09:23:48 -04:00
2022-04-30 09:41:17 -04:00
// Notify players
2023-07-15 09:23:48 -04:00
ChatSystemMsg msg = new ChatSystemMsg ( null , "[Bane Channel]" + this . getCity (). getGuild (). getName () + " has rallied against " + this . getOwner (). getGuild (). getName () + ". The siege on " + this . getCity (). getCityName () + " has been broken!" );
msg . setMessageType ( 4 );
msg . setChannel ( engine . Enum . ChatChannelType . SYSTEM . getChannelID ());
DispatchMessage . dispatchMsgToAll ( msg );
2022-04-30 09:41:17 -04:00
}
2023-07-15 09:23:48 -04:00
2022-04-30 09:41:17 -04:00
break ;
case CAPTURE :
// Push event to warehouse
BaneRecord . updateResolution ( this , Enum . RecordEventType . CAPTURE );
baneRemoved = this . remove ();
if ( baneRemoved ) {
2023-07-15 09:23:48 -04:00
ChatSystemMsg msg = new ChatSystemMsg ( null , "[Bane Channel]" + this . getOwner (). getGuild (). getName () + " have defeated " + this . getCity (). getGuild (). getName () + " and captured " + this . getCity (). getCityName () + '!' );
msg . setMessageType ( 4 );
msg . setChannel ( engine . Enum . ChatChannelType . SYSTEM . getChannelID ());
DispatchMessage . dispatchMsgToAll ( msg );
2022-04-30 09:41:17 -04:00
}
break ;
case DESTROY :
// Push event to warehouse
BaneRecord . updateResolution ( this , Enum . RecordEventType . DESTROY );
baneRemoved = this . remove ();
if ( baneRemoved ) {
2023-07-15 09:23:48 -04:00
ChatSystemMsg msg = new ChatSystemMsg ( null , "[Bane Channel]" + this . getOwner (). getGuild (). getName () + " have defeated " + this . getCity (). getGuild (). getName () + " and razed " + this . getCity (). getCityName () + '!' );
msg . setMessageType ( 4 );
msg . setChannel ( engine . Enum . ChatChannelType . SYSTEM . getChannelID ());
DispatchMessage . dispatchMsgToAll ( msg );
2022-04-30 09:41:17 -04:00
}
break ;
}
Zone cityZone = this . getCity (). getParent ();
if ( cityZone == null )
2023-07-15 09:23:48 -04:00
return ;
2022-04-30 09:41:17 -04:00
//UNPROTECT ALL SIEGE EQUIPMENT AFTER A BANE
2023-07-15 09:23:48 -04:00
for ( Building toUnprotect : cityZone . zoneBuildingSet ) {
if ( toUnprotect . getBlueprint () != null && toUnprotect . getBlueprint (). isSiegeEquip () && toUnprotect . assetIsProtected () == true )
toUnprotect . setProtectionState ( ProtectionState . NONE );
2022-04-30 09:41:17 -04:00
}
}
public boolean isErrant () {
boolean isErrant = true ;
if ( this . getOwner () == null )
return isErrant ;
2023-07-15 09:23:48 -04:00
2023-01-21 09:28:30 -05:00
if ( this . getOwner (). getGuild (). isEmptyGuild () == true )
2022-04-30 09:41:17 -04:00
return isErrant ;
2023-01-21 09:28:30 -05:00
if ( this . getOwner (). getGuild (). getNation (). isEmptyGuild () == true )
2022-04-30 09:41:17 -04:00
return isErrant ;
// Bane passes validation
isErrant = false ;
return isErrant ;
}
/**
* @return the stone
*/
public Building getStone () {
return BuildingManager . getBuilding ( this . stoneUUID );
}
/**
* @return the cityUUID
*/
public int getCityUUID () {
return cityUUID ;
}
}