mirror of
https://github.com/ProjectSWGCore/Holocore.git
synced 2026-10-05 20:13:20 -04:00
372 lines
15 KiB
Java
372 lines
15 KiB
Java
package services.galaxy;
|
|
|
|
import java.sql.ResultSet;
|
|
import java.sql.SQLException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
|
|
import network.packets.Packet;
|
|
import network.packets.swg.zone.EnterTicketPurchaseModeMessage;
|
|
import network.packets.swg.zone.PlanetTravelPointListRequest;
|
|
import network.packets.swg.zone.PlanetTravelPointListResponse;
|
|
import intents.chat.ChatBroadcastIntent;
|
|
import intents.network.GalacticPacketIntent;
|
|
import intents.object.ObjectTeleportIntent;
|
|
import intents.travel.*;
|
|
import resources.Location;
|
|
import resources.Terrain;
|
|
import resources.TravelPoint;
|
|
import resources.client_info.ClientFactory;
|
|
import resources.client_info.visitors.DatatableData;
|
|
import resources.control.Intent;
|
|
import resources.control.Service;
|
|
import resources.objects.SWGObject;
|
|
import resources.objects.creature.CreatureObject;
|
|
import resources.player.Player;
|
|
import resources.server_info.RelationalServerData;
|
|
import resources.sui.ISuiCallback;
|
|
import resources.sui.SuiButtons;
|
|
import resources.sui.SuiEvent;
|
|
import resources.sui.SuiListBox;
|
|
import resources.sui.SuiListBox.SuiListBoxItem;
|
|
import resources.sui.SuiMessageBox;
|
|
import services.objects.ObjectManager;
|
|
|
|
public final class TravelService extends Service {
|
|
|
|
private static final String DBTABLENAME = "travel";
|
|
private static final String TRAVELPOINTSFORPLANET = "SELECT name, x, y, z FROM " + DBTABLENAME + " WHERE planet=";
|
|
private static final byte PLANETNAMESCOLUMNINDEX = 0;
|
|
private static final short TICKETUSERADIUS = 50; // The distance a player needs to be within in order to use their ticket
|
|
|
|
private final ObjectManager objectManager;
|
|
private final RelationalServerData travelPointDatabase;
|
|
private final Map<Terrain, Collection<TravelPoint>> travelPoints;
|
|
|
|
public TravelService(ObjectManager objectManager) {
|
|
this.objectManager = objectManager;
|
|
|
|
travelPointDatabase = new RelationalServerData("serverdata/static/travel.db");
|
|
travelPoints = new HashMap<>();
|
|
|
|
if(!travelPointDatabase.linkTableWithSdb(DBTABLENAME, "serverdata/static/travel.sdb")) {
|
|
throw new main.ProjectSWG.CoreException("Unable to load sdb files for TravelService");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean initialize() {
|
|
registerForIntent(TravelPointSelectionIntent.TYPE);
|
|
registerForIntent(GalacticPacketIntent.TYPE);
|
|
registerForIntent(TicketPurchaseIntent.TYPE);
|
|
registerForIntent(TicketUseIntent.TYPE);
|
|
|
|
return super.initialize() && loadTravelPoints();
|
|
}
|
|
|
|
@Override
|
|
public boolean terminate() {
|
|
return super.terminate() && travelPointDatabase.close();
|
|
}
|
|
|
|
@Override
|
|
public void onIntentReceived(Intent i) {
|
|
switch(i.getType()) {
|
|
case TravelPointSelectionIntent.TYPE: handlePointSelection((TravelPointSelectionIntent) i); break;
|
|
case GalacticPacketIntent.TYPE: handleTravelPointRequest((GalacticPacketIntent) i); break;
|
|
case TicketPurchaseIntent.TYPE: handleTicketPurchase((TicketPurchaseIntent) i); break;
|
|
case TicketUseIntent.TYPE: handleTicketUse((TicketUseIntent) i); break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Travel points are loaded from /serverdata/static/travel.sdb
|
|
* A travel point represents a travel destination.
|
|
* @author Mads
|
|
* @return true if all points were loaded succesfully and false if not.
|
|
*/
|
|
private boolean loadTravelPoints() {
|
|
boolean success = true;
|
|
|
|
DatatableData travelFeeTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/travel/travel.iff");
|
|
Map<String, Integer> planetFees;
|
|
|
|
// Load the planet names
|
|
String[] planetNames = new String[travelFeeTable.getRowCount()];
|
|
|
|
travelFeeTable.handleRows(currentRow -> planetNames[currentRow] = (String) travelFeeTable.getCell(currentRow, PLANETNAMESCOLUMNINDEX));
|
|
|
|
for(int i = 0; i < travelFeeTable.getRowCount(); i++) {
|
|
String planetName = planetNames[i];
|
|
|
|
try(ResultSet travelPointTable = travelPointDatabase.prepareStatement(TRAVELPOINTSFORPLANET + "'" + planetName + "'").executeQuery()) {
|
|
planetFees = new HashMap<>();
|
|
|
|
for(int j = 0; j < travelFeeTable.getRowCount(); j++) {
|
|
planetFees.put((String) planetNames[j], (int) travelFeeTable.getCell(j, travelFeeTable.getColumnFromName(planetName)));
|
|
System.out.println("Going from " + planetName + " to " + planetNames[j] + " costs " + travelFeeTable.getCell(j, travelFeeTable.getColumnFromName(planetName)));
|
|
}
|
|
Terrain terrain = Terrain.getTerrainFromName(planetName);
|
|
|
|
while(travelPointTable.next()) {
|
|
Location loc = new Location(travelPointTable.getDouble("x"), travelPointTable.getDouble("y"), travelPointTable.getDouble("z"), terrain);
|
|
TravelPoint tp = new TravelPoint(travelPointTable.getString("name"), loc, planetFees, 0);
|
|
Collection<TravelPoint> travelPointsForPlanet = travelPoints.get(terrain);
|
|
|
|
if(travelPointsForPlanet == null) {
|
|
travelPointsForPlanet = new ArrayList<>();
|
|
|
|
travelPoints.put(terrain, travelPointsForPlanet);
|
|
}
|
|
|
|
travelPointsForPlanet.add(tp);
|
|
}
|
|
} catch(SQLException e) {
|
|
e.printStackTrace();
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
private void handlePointSelection(TravelPointSelectionIntent tpsi) {
|
|
CreatureObject traveler = tpsi.getCreature();
|
|
|
|
traveler.sendSelf(new EnterTicketPurchaseModeMessage(traveler.getTerrain().getName(), nearestTravelPoint(traveler.getWorldLocation()).getName(), tpsi.isInstant()));
|
|
}
|
|
|
|
private void handleTravelPointRequest(GalacticPacketIntent i) {
|
|
Packet p = i.getPacket();
|
|
|
|
if(p instanceof PlanetTravelPointListRequest ) {
|
|
PlanetTravelPointListRequest req = (PlanetTravelPointListRequest) p;
|
|
|
|
i.getPlayerManager().getPlayerFromNetworkId(i.getNetworkId()).sendPacket(new PlanetTravelPointListResponse(req.getPlanetName(), travelPoints.get(Terrain.getTerrainFromName(req.getPlanetName()))));
|
|
}
|
|
}
|
|
|
|
private void handleTicketPurchase(TicketPurchaseIntent i) {
|
|
CreatureObject purchaser = i.getPurchaser();
|
|
Location purchaserWorldLocation = purchaser.getWorldLocation();
|
|
TravelPoint nearestPoint = nearestTravelPoint(purchaserWorldLocation);
|
|
TravelPoint destinationPoint = destinationPoint(purchaserWorldLocation.getTerrain(), i.getDestinationName());
|
|
String suiMessage = "@travel:";
|
|
Player purchaserOwner = purchaser.getOwner();
|
|
boolean roundTrip = i.isRoundTrip();
|
|
|
|
if(nearestPoint == null || destinationPoint == null)
|
|
return;
|
|
|
|
int purchaserBankBalance = purchaser.getBankBalance();
|
|
int ticketPrice = nearestPoint.totalTicketPrice(i.getDestinationPlanet());
|
|
|
|
if(roundTrip)
|
|
ticketPrice += destinationPoint.totalTicketPrice(nearestPoint.getLocation().getTerrain().getName());
|
|
|
|
if(ticketPrice > purchaserBankBalance) {
|
|
// Make the message in the SUI window reflect the fail
|
|
suiMessage += "short_funds";
|
|
} else {
|
|
// Make the message in the SUI window reflect the success
|
|
suiMessage += "ticket_purchase_complete";
|
|
|
|
// Also send the purchaser a system message
|
|
// TODO is there a STF containing this?
|
|
new ChatBroadcastIntent(purchaserOwner, String.format("You succesfully make a payment of %d credits to the Galactic Travel Commission.", ticketPrice)).broadcast();
|
|
|
|
purchaser.setBankBalance(purchaserBankBalance - ticketPrice);
|
|
|
|
// Create the ticket object
|
|
SWGObject ticket = objectManager.createObject("object/tangible/travel/travel_ticket/base/shared_base_travel_ticket.iff", false);
|
|
|
|
// Departure attributes
|
|
ticket.addAttribute("@obj_attr_n:travel_departure_planet", "@planet_n:" + purchaserWorldLocation.getTerrain().getName().toLowerCase(Locale.ENGLISH));
|
|
ticket.addAttribute("@obj_attr_n:travel_departure_point", nearestPoint.getName());
|
|
|
|
// Arrival attributes
|
|
ticket.addAttribute("@obj_attr_n:travel_arrival_planet", "@planet_n:" + destinationPoint.getLocation().getTerrain().getName().toLowerCase(Locale.ENGLISH));
|
|
ticket.addAttribute("@obj_attr_n:travel_arrival_point", destinationPoint.getName());
|
|
|
|
// Put the ticket in their inventory
|
|
purchaser.getSlottedObject("inventory").addObject(ticket);
|
|
|
|
if(roundTrip) {
|
|
// Create the return ticket object
|
|
SWGObject returnTicket = objectManager.createObject("object/tangible/travel/travel_ticket/base/shared_base_travel_ticket.iff", false);
|
|
|
|
// Departure attributes
|
|
returnTicket.addAttribute("@obj_attr_n:travel_departure_planet", "@planet_n:" + destinationPoint.getLocation().getTerrain().getName().toLowerCase(Locale.ENGLISH));
|
|
returnTicket.addAttribute("@obj_attr_n:travel_departure_point", destinationPoint.getName());
|
|
|
|
// Arrival attributes
|
|
returnTicket.addAttribute("@obj_attr_n:travel_arrival_planet", "@planet_n:" + purchaserWorldLocation.getTerrain().getName().toLowerCase(Locale.ENGLISH));
|
|
returnTicket.addAttribute("@obj_attr_n:travel_arrival_point", nearestPoint.getName());
|
|
|
|
// Put the ticket in their inventory
|
|
purchaser.getSlottedObject("inventory").addObject(ticket);
|
|
}
|
|
}
|
|
|
|
// Create the SUI window
|
|
SuiMessageBox messageBox = new SuiMessageBox(SuiButtons.OK, "STAR WARS GALAXIES", suiMessage);
|
|
|
|
// Display the window to the purchaser
|
|
messageBox.display(purchaserOwner);
|
|
}
|
|
|
|
private void handleTicketUse(TicketUseIntent i) {
|
|
if(i.getTicket() == null)
|
|
handleTicketUseSui(i);
|
|
else
|
|
handleTicketUseClick(i);
|
|
}
|
|
|
|
private void handleTicketUseSui(TicketUseIntent i) {
|
|
Player player = i.getPlayer();
|
|
CreatureObject creature = player.getCreatureObject();
|
|
Collection<SWGObject> tickets = creature.getItemsByTemplate("inventory", "object/tangible/travel/travel_ticket/base/shared_base_travel_ticket.iff");
|
|
SuiListBox destinationSelection;
|
|
List<SWGObject> usableTickets = new ArrayList<>();
|
|
|
|
for(SWGObject ticket : tickets)
|
|
if(objectHasTicketAttributes(ticket))
|
|
if(ticketCanBeUsedAtNearestPoint(ticket))
|
|
usableTickets.add(ticket);
|
|
|
|
if(usableTickets.isEmpty()) // They don't have a valid ticket.
|
|
new ChatBroadcastIntent(player, "@travel:no_ticket_for_shuttle").broadcast();
|
|
else {
|
|
destinationSelection = new SuiListBox(SuiButtons.OK_CANCEL, "@travel:select_destination", "@travel:select_destination");
|
|
|
|
for(SWGObject usableTicket : usableTickets) {
|
|
TravelPoint destinationPoint = destinationPoint(usableTicket);
|
|
|
|
destinationSelection.addListItem(destinationPoint.toString(), destinationPoint);
|
|
}
|
|
|
|
destinationSelection.addOkButtonCallback("handleSelectedItem", new DestinationSelectionSuiCallback(destinationSelection, usableTickets));
|
|
destinationSelection.display(player);
|
|
}
|
|
}
|
|
|
|
private void handleTicketUseClick(TicketUseIntent i) {
|
|
CreatureObject traveler = i.getPlayer().getCreatureObject();
|
|
Location worldLoc = traveler.getWorldLocation();
|
|
TravelPoint nearestPoint = nearestTravelPoint(worldLoc);
|
|
double distanceToNearestPoint = worldLoc.distanceTo(nearestPoint.getLocation());
|
|
SWGObject ticket = i.getTicket();
|
|
Player player = i.getPlayer();
|
|
|
|
if(objectHasTicketAttributes(ticket)) {
|
|
if(ticketCanBeUsedAtNearestPoint(ticket)) {
|
|
if(distanceToNearestPoint <= TICKETUSERADIUS) {
|
|
// They can use their ticket if they're within range.
|
|
teleportAndDestroyTicket(destinationPoint(ticket), ticket, traveler);
|
|
} else {
|
|
// They're out of range - let them know.
|
|
new ChatBroadcastIntent(player, "@travel:boarding_too_far").broadcast();
|
|
}
|
|
} else {
|
|
// This ticket isn't valid for this point
|
|
new ChatBroadcastIntent(player, "@travel:wrong_shuttle").broadcast();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void teleportAndDestroyTicket(TravelPoint destination, SWGObject ticket, CreatureObject traveler) {
|
|
objectManager.destroyObject(ticket);
|
|
|
|
new ObjectTeleportIntent(traveler, destination.getLocation()).broadcast();
|
|
}
|
|
|
|
private boolean objectHasTicketAttributes(SWGObject object) {
|
|
String departurePlanet = object.getAttribute("@obj_attr_n:travel_departure_planet");
|
|
String departureDestination = object.getAttribute("@obj_attr_n:travel_departure_point");
|
|
String arrivalPlanet = object.getAttribute("@obj_attr_n:travel_arrival_planet");
|
|
String arrivalPoint = object.getAttribute("@obj_attr_n:travel_arrival_point");
|
|
|
|
return departurePlanet != null && departureDestination != null && arrivalPlanet != null && arrivalPoint != null;
|
|
}
|
|
|
|
private boolean ticketCanBeUsedAtNearestPoint(SWGObject ticket) {
|
|
CreatureObject ticketOwner = ticket.getOwner().getCreatureObject();
|
|
Location worldLoc = ticketOwner.getWorldLocation();
|
|
TravelPoint nearest = nearestTravelPoint(worldLoc);
|
|
String departurePoint = ticket.getAttribute("@obj_attr_n:travel_departure_point");
|
|
String departurePlanet = ticket.getAttribute("@obj_attr_n:travel_departure_planet");
|
|
Terrain departureTerrain = Terrain.getTerrainFromName(departurePlanet.split(":")[1]);
|
|
Terrain currentTerrain = worldLoc.getTerrain();
|
|
|
|
return departureTerrain == currentTerrain && departurePoint.equals(nearest.getName());
|
|
}
|
|
|
|
private TravelPoint destinationPoint(SWGObject ticket) {
|
|
return destinationPoint(Terrain.getTerrainFromName(ticket.getAttribute("@obj_attr_n:travel_arrival_planet").split(":")[1]), ticket.getAttribute("@obj_attr_n:travel_arrival_point"));
|
|
}
|
|
|
|
private TravelPoint destinationPoint(Terrain terrain, String pointName) {
|
|
TravelPoint currentResult = null;
|
|
Iterator<TravelPoint> pointIterator = travelPoints.get(terrain).iterator();
|
|
|
|
while(pointIterator.hasNext()) {
|
|
TravelPoint candidate = pointIterator.next();
|
|
|
|
if(candidate.getName().equals(pointName)) {
|
|
currentResult = candidate;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return currentResult;
|
|
}
|
|
|
|
private TravelPoint nearestTravelPoint(Location objectLocation) {
|
|
TravelPoint currentResult = null;
|
|
double currentResultDistance = Double.MAX_VALUE;
|
|
double candidateDistance;
|
|
Collection<TravelPoint> pointsForPlanet = travelPoints.get(objectLocation.getTerrain());
|
|
|
|
for(TravelPoint candidate : pointsForPlanet) {
|
|
|
|
if(currentResult == null) { // Will occur upon the first iteration.
|
|
currentResult = candidate; // The first candidate will always be the first possible result.
|
|
} else {
|
|
candidateDistance = candidate.getLocation().distanceTo(objectLocation);
|
|
|
|
if(candidateDistance < currentResultDistance) {
|
|
currentResult = candidate;
|
|
currentResultDistance = candidateDistance;
|
|
}
|
|
}
|
|
}
|
|
return currentResult;
|
|
}
|
|
|
|
private class DestinationSelectionSuiCallback implements ISuiCallback {
|
|
|
|
private final SuiListBox destinationSelection;
|
|
private final List<SWGObject> usableTickets;
|
|
|
|
private DestinationSelectionSuiCallback(SuiListBox destinationSelection, List<SWGObject> usableTickets) {
|
|
this.destinationSelection = destinationSelection;
|
|
this.usableTickets = usableTickets;
|
|
}
|
|
|
|
@Override
|
|
public void handleEvent(Player player, SWGObject actor, SuiEvent event,
|
|
Map<String, String> parameters) {
|
|
int selection = SuiListBox.getSelectedRow(parameters);
|
|
SuiListBoxItem selectedItem = destinationSelection.getListItem(selection);
|
|
TravelPoint selectedDestination = (TravelPoint) selectedItem.getObject();
|
|
|
|
teleportAndDestroyTicket(selectedDestination, usableTickets.get(selection), player.getCreatureObject());
|
|
}
|
|
}
|
|
}
|