DictatorAI/air/ 0000755 0001750 0000144 00000000000 12202257175 012657 5 ustar krinn users DictatorAI/air/airportbuilder.nut 0000644 0001750 0000144 00000047677 12201730052 016450 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cBuilder::AirportNeedUpgrade(stationid)
// this upgrade an existing airport to a newer one
{
// better check criticals stuff before stopping our traffic and find we're going to fail
local station=cStation.Load(stationid);
local firstroute = false;
if (station != false)
{
foreach (owner, _ in station.s_Owner)
{
firstroute = cRoute.Load(owner);
if (firstroute != false && firstroute.Status == RouteStatus.WORKING) break;
}
}
if (!firstroute || !station || firstroute.Status != RouteStatus.WORKING) { DInfo("Found an airport attach to no route ! Giving up.",1); return false }
local townrating=0;
local noiselevel=0;
local townid=-1;
local start=null;
cDebug.ClearSigns();
if (firstroute.SourceStation.s_ID == stationid) { start=true; townid=firstroute.SourceProcess.ID; cDebug.PutSign(firstroute.SourceStation.s_Location, "Upgrade"); }
if (firstroute.TargetStation.s_ID == stationid) { start=false; townid=firstroute.TargetProcess.ID; cDebug.PutSign(firstroute.TargetStation.s_Location, "Upgrade"); }
if (start == null) { DError("Cannot find station from owner for stationid "+stationid,1); return false; }
local now=AIDate.GetCurrentDate();
if (station.s_DateLastUpgrade != null && now - station.s_DateLastUpgrade < 300) // wait 300 days before each trys
{
DInfo("We try to upgrade that airport not so long ago, giving up",1);
return false;
}
station.s_DateLastUpgrade=now;
if (!cBanker.CanBuyThat(station.s_MoneyUpgrade))
{
DInfo("We still lack money to upgrade the airport. Will retry later with "+station.s_MoneyUpgrade+" credits",1);
station.s_MoneyUpgrade = station.s_MoneyUpgrade / 2; // we decrease it, in case we overestimate the cost
return false;
}
local airporttype=INSTANCE.main.builder.GetAirportType();
townrating=AITown.GetRating(townid,AICompany.COMPANY_SELF);
noiselevel=AITown.GetAllowedNoise(townid);
local ourloc=0;
local ournoise=AIAirport.GetNoiseLevelIncrease(station.s_Location,airporttype);
DInfo("Town rating = "+townrating+" noiselevel="+noiselevel,2);
cTileTools.SeduceTown(townid);
townrating=AITown.GetRating(townid, AICompany.COMPANY_SELF);
if (townrating < AITown.TOWN_RATING_GOOD)
{ DInfo("Cannot upgrade airport, too dangerous with our current rating with this town.",1); station.s_DateLastUpgrade+=70; return false; }
local cost=AIAirport.GetPrice(airporttype)*cBanker.GetInflationRate();
cost+=1000; // i'm not sure how much i need to destroy old airport
INSTANCE.main.bank.RaiseFundsBy(cost);
if (AICompany.GetBankBalance(AICompany.COMPANY_SELF) < cost)
{ DInfo("Cannot upgrade airport, need "+cost+" money for success.",1); return false; }
DInfo("Trying to upgrade airport #"+stationid+" "+station.s_Name,0);
// find traffic that use that airport & reroute it
// prior to reroute aircraft, make sure they have a route to go
station.s_UpgradeTry --;
foreach (ownID, dummy in station.s_Owner)
{
local dummyObj=cRoute.Load(ownID);
if (!dummyObj) continue;
INSTANCE.main.carrier.VehicleBuildOrders(dummyObj.GroupID,true);
}
INSTANCE.main.carrier.AirNetworkOrdersHandler(); // or maybe it's one from our network that need orders
local counter=0;
local maxcount=100;
local result=false;
INSTANCE.main.carrier.VehicleHandleTrafficAtStation(station.s_ID, true);
cBuilder.CloseAirport(station.s_ID);
// time to pray a bit for success, we could invalidate a working route here
do {
local test=AITestMode();
result=AIAirport.RemoveAirport(station.s_Location);
test=null;
counter++;
if (!result)
{
AIController.Sleep(40);
INSTANCE.main.carrier.FreeDepotOfVehicle(station.s_Depot); // try remove aircraft from airport
}
} while (AICompany.GetBankBalance(AICompany.COMPANY_SELF) > 1000 && !result && counter < maxcount);
result=INSTANCE.main.builder.BuildAirStation(start, firstroute.UID);
if (result == -1) cBuilder.OpenAirport(station.s_ID);
else cBuilder.OpenAirport(result);
INSTANCE.main.carrier.VehicleHandleTrafficAtStation(station.s_ID,false);
if (result == -1) return false;
DInfo("Airport was upgrade successfuly !",1);
}
function cBuilder::CloseAirport(stationID)
{
if (AIStation.IsAirportClosed(stationID)) return false;
return AIStation.OpenCloseAirport(stationID);
}
function cBuilder::OpenAirport(stationID)
{
if (AIStation.IsAirportClosed(stationID)) return AIStation.OpenCloseAirport(stationID);
}
function cBuilder::GetAirportType()
// return an airport type to build or null
{
local AirType=null;
if (AIAirport.IsValidAirportType(AIAirport.AT_SMALL)) { AirType=AIAirport.AT_SMALL; }
if (AIAirport.IsValidAirportType(AIAirport.AT_LARGE)) { AirType=AIAirport.AT_LARGE; }
if (AIAirport.IsValidAirportType(AIAirport.AT_METROPOLITAN)) { AirType=AIAirport.AT_METROPOLITAN; }
if (AIAirport.IsValidAirportType(AIAirport.AT_INTERNATIONAL)) { AirType=AIAirport.AT_INTERNATIONAL; }
if (AIAirport.IsValidAirportType(AIAirport.AT_INTERCON)) { AirType=AIAirport.AT_INTERCON; }
return AirType;
}
function cBuilder::AirportMaker(tile, airporttype)
// Build an airport at tilebase
{
local essai=false;
local w = AIAirport.GetAirportWidth(airporttype);
local h = AIAirport.GetAirportHeight(airporttype);
local tiles = AITileList();
tiles.AddRectangle(tile, tile+AIMap.GetTileIndex(w-1,h-1));
tiles.Valuate(AITile.IsWaterTile);
tiles.KeepValue(1);
foreach (tile, _ in tiles) { AITile.DemolishTile(tile); }
if (!cTileTools.SeduceTown(AITile.GetClosestTown(tile)))
{
DInfo("Town doesn't like us...",1);
cTileTools.terraformCost.AddItem(999995,1); // tell rating is poor
}
DInfo("Cost to build an airport = "+AIAirport.GetPrice(airporttype)*cBanker.GetInflationRate(),2);
INSTANCE.main.bank.RaiseFundsBy(AIAirport.GetPrice(airporttype)*cBanker.GetInflationRate());
essai=AIAirport.BuildAirport(tile, airporttype, AIStation.STATION_NEW);
if (essai) DInfo("-> Built an airport at "+tile,1);
else DError("Cannot build an airport at "+tile,1);
return essai;
}
function cBuilder::BuildAirStation(start, routeID=null)
// Create an airport for our route at start/destination
// return airport stationID on success
{
local road=false;
if (routeID == null) { if (INSTANCE.main.builder.building_route != -1) road=INSTANCE.main.route; }
else road=cRoute.Load(routeID);
if (road == false) return -1;
local townname="none";
local helipadonly=false;
cError.ClearError(); // make sure we clear previous failure
local airporttype=cBuilder.GetAirportType();
local air_x=AIAirport.GetAirportWidth(airporttype);
local air_y=AIAirport.GetAirportHeight(airporttype);
local rad=AIAirport.GetAirportCoverageRadius(airporttype);
local cargoID=cCargo.GetPassengerCargo();
local ignoreList=AIList();
local oldAirport = null;
local newAirport = null;
local oldAirport_Remove=false;
local airportUpgrade=false;
local oldAirport_Noise=0;
local oldAirport_Width=0;
local oldAirport_Height=0;
local Sameplace = false;
local tilelist=AITileList();
local success=false;
local allfail=true;
local newStation=-1;
local townnoise=0;
local heliloc=null;
local needTime=false;
local needMoney=0;
local solverlist=AITileList();
local townID=null;
if (start)
{
if (road.SourceProcess.IsTown)
{
tilelist= cTileTools.GetTilesAroundTown(road.SourceProcess.ID);
helipadonly=false;
townname=road.SourceProcess.Name;
townnoise=AITown.GetAllowedNoise(road.SourceProcess.ID);
oldAirport = (typeof(road.SourceStation) == "instance") ? road.SourceStation : null;
townID=road.SourceProcess.ID;
}
else {
// no coverage need, we know exactly where we go
helipadonly=true;
heliloc=road.SourceProcess.Location;
}
}
else {
if (road.TargetProcess.IsTown)
{
tilelist= cTileTools.GetTilesAroundTown(road.TargetProcess.ID);
townname= road.TargetProcess.Name;
townnoise=AITown.GetAllowedNoise(road.TargetProcess.ID);
helipadonly=false;
oldAirport = (typeof(road.TargetStation) == "instance") ? road.TargetStation : null;
townID=road.TargetProcess.ID;
}
else return -1;
// we should never have a platform as destination station !!!
}
if (oldAirport != null)
{
airportUpgrade=true;
oldAirport_Width=AIAirport.GetAirportWidth(oldAirport.s_SubType);
oldAirport_Height=AIAirport.GetAirportHeight(oldAirport.s_SubType);
if (air_x == oldAirport_Width && air_y == oldAirport_Height) Sameplace=true;
ignoreList=cTileTools.FindStationTiles(oldAirport.s_Location);
oldAirport_Noise=AIAirport.GetNoiseLevelIncrease(oldAirport.s_Location, oldAirport.s_SubType);
cDebug.showLogic(ignoreList);
DInfo("Found an old airport in town "+oldAirport.s_Name+": we will upgrade it",1);
if (!AIAirport.IsValidAirportType(oldAirport.s_SubType)) DWarn("Old airport type is no more buildable, this is highly dangerous !!!",0);
}
if (!helipadonly)
{
DInfo("Looking for a place to build an airport at "+townname,0);
tilelist.Valuate(cTileTools.IsBuildable);
tilelist.RemoveValue(0);
local worktilelist=AIList();
worktilelist.AddList(tilelist);
worktilelist.Valuate(AIAirport.GetNoiseLevelIncrease,airporttype);
worktilelist.RemoveAboveValue((townnoise + oldAirport_Noise));
DInfo("Town "+townname+" noise level="+townnoise+" oldairportnoise="+oldAirport_Noise,2);
if (worktilelist.IsEmpty())
{
DInfo("Town "+townname+" can only get a noise level of "+townnoise+" Giving up.",0);
cError.RaiseError();
return -1;
}
worktilelist.Clear();
foreach (tile, dummy in tilelist)
{
local newTile=cTileTools.IsBuildableRectangle(tile, air_x, air_y, ignoreList);
if (newTile != -1) worktilelist.AddItem(newTile,666);
}
tilelist.Clear();
tilelist.AddList(worktilelist);
tilelist.Valuate(AIAirport.GetNearestTown, airporttype);
tilelist.KeepValue(townID);
tilelist.Valuate(cTileTools.IsTilesBlackList);
tilelist.KeepValue(0);
if (tilelist.IsEmpty())
{
DInfo("There's no buildable space at "+townname+" where i could put an airport of "+air_x+"x"+air_y,0);
cError.RaiseError();
return -1;
}
tilelist.Valuate(AITile.GetCargoAcceptance, cargoID, air_x, air_y, rad);
tilelist.RemoveBelowValue(8);
tilelist.Sort(AIList.SORT_BY_VALUE, false);
solverlist.AddList(tilelist);
tilelist.Valuate(AITile.IsWaterTile);
tilelist.RemoveValue(1);
tilelist.Valuate(AITile.GetCargoAcceptance, cargoID, air_x, air_y, rad);
tilelist.RemoveBelowValue(8);
if (Sameplace) { tilelist.Clear(); tilelist.AddItem(oldAirport.s_Location,0); }
foreach (tile, dummy in tilelist)
{
local newTile=-1;
if (cTileTools.IsAreaFlat(tile, air_x, air_y)) newTile=tile;
if (Sameplace) newTile = tile;
if (newTile != -1)
{
DInfo("Found a flat area to try at "+newTile,1);
cDebug.PutSign(newTile,"*");
for (local tt=0; tt < 50; tt++)
{
if (airportUpgrade && !oldAirport_Remove)
{
cCarrier.FreeDepotOfVehicle(oldAirport.s_Depot);
oldAirport_Remove=AIAirport.RemoveAirport(oldAirport.s_Location);
DInfo("Removing old airport : "+oldAirport.s_Name,1);
if (oldAirport_Remove) { break; }
}
else break;
AIController.Sleep(10);
}
if (airportUpgrade && !oldAirport_Remove) { needTime=true; break; }
success=cBuilder.AirportMaker(newTile, airporttype);
if (!success && cError.IsCriticalError()) break;
if (success) { newStation=newTile; break; }
else if (cTileTools.terraformCost.HasItem(999995))
{
cTileTools.terraformCost.RemoveItem(999995);
needTime=true;
break;
}
local pause = cLooper();
}
}
if (!success && !needTime)
{
// Switch back to simple list of tile, solver work less hard to find a solve
DInfo("Analyzing the surrounding of "+townname,0);
local solver=cBuilder.AirportBestPlace_EvaluateHill(solverlist, air_x, air_y);
if (solver.len() == 0) { DWarn("Nothing to do, we can't find any solve to build the airport",1); }
else {
for (local tt=0; tt < 50; tt++)
{
if (cError.IsError()) break;
if (airportUpgrade && !oldAirport_Remove)
{
cCarrier.FreeDepotOfVehicle(oldAirport.s_Depot);
oldAirport_Remove=AIAirport.RemoveAirport(oldAirport.s_Location);
DInfo("Removing old airport : "+oldAirport.s_Name,1);
if (oldAirport_Remove) break;
}
else break;
AIController.Sleep(10);
}
if (airportUpgrade && !oldAirport_Remove) { needTime=true; }
if (!needTime)
{
newStation=cBuilder.AirportBestPlace_BuildFromSolve(solver, air_x, air_y, airporttype);
if (cTileTools.terraformCost.HasItem(999999))
{
needMoney=cTileTools.terraformCost.GetValue(999999);
cTileTools.terraformCost.RemoveItem(999999);
}
}
}
}
}
success= (newStation != -1 && !needTime);
if (helipadonly) success=true;
if (needMoney > 0 && oldAirport != null)
{
oldAirport.s_MoneyUpgrade = needMoney;
}
if (!success)
{
DWarn("Failure to build an airport at "+townname,0);
if (oldAirport_Remove)
{
DInfo("Trying to restore previous airport, feeling lucky ?",0);
success=cBuilder.AirportMaker(oldAirport.s_Location, oldAirport.s_SubType);
if (success) { cError.ClearError(); return oldAirport.s_ID; }
else {
foreach (ownerUID, _ in oldAirport.s_Owner)
{
oldAirport.OwnerReleaseStation(ownerUID);
local deadowner=cRoute.Load(ownerUID);
if (deadowner != false) { DInfo("BuildAirStation mark "+deadowner.UID+" undoable",1); deadowner.RouteIsNotDoable(); }
}
// and get ride of the old (now dead) station
cStation.DeleteStation(oldAirport.s_ID);
return -1;
}
}
}
if (success)
{
if (!helipadonly)
{
DInfo("Airport #"+AIStation.GetStationID(newStation)+"-"+AIStation.GetName(AIStation.GetStationID(newStation))+" built at "+townname,0);
local fakeroute = cRoute();
fakeroute.SourceStation = AIStation.GetStationID(newStation);
newStation = fakeroute.CreateNewStation(true);
if (oldAirport_Remove)
{
foreach (ownerUID, _ in oldAirport.s_Owner) cRoute.RouteChangeStation(ownerUID, oldAirport, newStation);
cCarrier.VehicleHandleTrafficAtStation(newStation.s_ID, false); // rebuild orders
}
else {
if (start)
{
road.SourceStation = newStation;
}
else {
road.TargetStation = newStation;
}
}
return newStation.s_ID;
}
else { // platform
road.SourceStation = AIStation.GetStationID(heliloc);
road.VehicleType = RouteType.CHOPPER;
road.SourceStation = road.CreateNewStation(start);
if (!cMisc.ValidInstance(road.SourceStation)) return -1;
road.SourceStation.s_SubType = -2;
road.SourceStation.s_Depot = -1;
road.SourceStation.s_Location = heliloc;
return road.SourceStation.s_ID;
}
}
if (!needTime && !needMoney) cError.RaiseError();
return -1;
}
function cBuilder::AirportBestPlace_EvaluateHill(workTileList, width, height)
// This search all tiles from a list of tiles where we can build an airport and record all solves found with their costs to build them
// This is real time consumming as we will run multi-times the TerraformSolver to flatten land
// workTileList : a list of tiles we would like an airport built
// width : width of an airport
// height: height of an airport
{
local allsolve=[];
if (workTileList.IsEmpty()) return [];
cDebug.showLogic(workTileList);
cDebug.ClearSigns();
local randomTile=AITileList();
randomTile.AddList(workTileList);
randomTile.Sort(AIList.SORT_BY_VALUE, false);
local prev= null;
foreach (tile, value in randomTile)
{
if (prev==null) { prev = tile; continue; }
else {
if (AIMap.DistanceManhattan(prev, tile) < 2) randomTile.SetValue(tile, value >> 1);
prev = tile;
}
}
//randomTile.Valuate(AIBase.RandItem);
randomTile.Sort(AIList.SORT_BY_VALUE, false);
cDebug.showLogic(randomTile);
cDebug.ClearSigns();
randomTile.KeepTop(6);
local ttr = AIList();
ttr.AddList(randomTile);
foreach (tile, dummy in ttr) randomTile.SetValue(tile, tile+AIMap.GetTileIndex(width-1, height-1));
workTileList.Clear();
workTileList.AddList(randomTile);
cDebug.showLogic(workTileList);
cDebug.ClearSigns();
local templist=AITileList();
local solveIndex=0;
foreach (tileFrom, tileTo in workTileList)
{
templist.Clear();
templist.AddRectangle(tileFrom, tileTo);
cDebug.showLogic(templist);
cDebug.PutSign(tileFrom,"F"); cDebug.PutSign(tileTo,"T");
local solve=cTileTools.TerraformHeightSolver(templist);
cDebug.ClearSigns();
solve.RemoveValue(0); // discard no solve
local bf, bt, bs, bp=null;
bp=1999999999;
foreach (solution, prize in solve)
{
/*allsolve.push(tileFrom);
allsolve.push(tileTo);
allsolve.push(solution);
allsolve.push(prize);*/
if (abs(prize) < abs(bp)) { bf=tileFrom; bt=tileTo; bp=prize; bs=solution; } // find cheapest one
solveIndex++;
}
if (!solve.IsEmpty() && bf != null)
{
allsolve.push(bf); // this way we only keep cheapest one out of all solves found for that area
allsolve.push(bt); // so the area will have only 1 solution
allsolve.push(bs);
allsolve.push(bp);
}
}
cDebug.ClearSigns();
DInfo("Total solves found: "+solveIndex,1);
return allsolve;
}
function cBuilder::AirportBestPlace_BuildFromSolve(allsolve, width, height, airporttype)
// This function try each solve found from AirportBestPlace_EvaluateHill to flatten area and build an airport
// Time consumming, and worst, money eater !
// solve = an array of solve, should have been made by AirportBestPlace_EvaluateHill
// width : width of an airport
// height: height of an airport
// airportype: the type of airport to build
{
local bestSolve=AIList();
local radius=AIAirport.GetAirportCoverageRadius(airporttype);
local solveIndex=0;
local cargoID=cCargo.GetPassengerCargo();
for (local i=0; i < allsolve.len(); i++)
{
local tileFrom, tileTo, solution, realprize =null;
tileFrom=allsolve[i+0];
tileTo=allsolve[i+1];
solution=allsolve[i+2];
realprize=allsolve[i+3];
local cargovalue=AITile.GetCargoProduction(tileFrom, cargoID, width, height, radius);
bestSolve.AddItem(solveIndex,(10000000000-(abs(realprize)*8))+(cargovalue));
i+=3;
solveIndex++;
}
bestSolve.Sort(AIList.SORT_BY_VALUE, false);
foreach (index, prize in bestSolve)
{
local tileFrom, tileTo, solution, realprize, updown=null;
tileFrom=allsolve[4*index+0];
tileTo=allsolve[4*index+1];
solution=allsolve[4*index+2];
realprize=allsolve[4*index+3];
updown=(realprize < 0);
local templist=AITileList();
templist.AddRectangle(tileFrom, tileTo);
cDebug.PutSign(tileFrom,"?");
if (!cBanker.CanBuyThat(abs(realprize)))
{
DInfo("Skipping that solve. We won't have enough money to succeed",1);
cTileTools.terraformCost.AddItem(999999,abs(prize));
cError.ClearError(); // make sure we tell it's just temporary
return -1; // no need to continue, list is range from cheaper to higher prize, if you can't buy cheaper already...
}
else {
cBanker.RaiseFundsBigTime();
local money=cTileTools.TerraformDoAction(templist, solution, updown, false);
if (money != -1)
{
DInfo("Trying to build an airport at "+tileFrom,1);
local success=INSTANCE.main.builder.AirportMaker(tileFrom, airporttype);
if (success) return tileFrom;
else if (cTileTools.terraformCost.HasItem(999995))
{
cTileTools.terraformCost.RemoveItem(999995);
return -1;
}
}
DInfo("Blacklisting tile "+tileFrom,2);
cTileTools.BlackListTile(tileFrom, -100);
}
}
return -1;
}
function cBuilder::AirportAcceptBigPlanes(airportID)
// return true if we can accept big aircraft
// airportID : the airport station ID
{
if (airportID == null) return false;
local airport = cStation.Load(airportID);
if (!airport) return false;
if (airport.s_SubType == AIAirport.AT_SMALL) return false;
return true;
}
DictatorAI/air/aircraftbuilder.nut 0000644 0001750 0000144 00000007525 12202257174 016561 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cCarrier::GetAirVehicle(routeidx, cargo = -1, modele = AircraftType.EFFICIENT)
// return the vehicle we will pickup if we build a vehicle on that route
// if eff = -1, get info from the routeidx, else we're in guess mode
{
local object = cEngineLib.Infos();
object.cargo_id = cargo;
object.engine_type = AIVehicle.VT_AIR;
if (cargo == -1)
{
local road = cRoute.Load(routeidx);
if (!road) return -1;
if (road.VehicleType == RouteType.AIRNET || road.VehicleType == RouteType.AIRNETMAIL) modele=AircraftType.BEST; // top speed/capacity for network
if (road.SourceStation.s_SubType == AIAirport.AT_SMALL || road.TargetStation.s_SubType == AIAirport.AT_SMALL) modele=20; // small
if (road.VehicleType == RouteType.CHOPPER) modele=AircraftType.CHOPPER; // need a chopper
object.cargo_id = road.CargoID;
object.depot = cRoute.GetDepot(routeidx);
}
object.bypass = modele; //passing wanted modele thru bypass
local veh = cEngineLib.GetBestEngine(object, cCarrier.VehicleFilterAir);
return veh[0];
}
function cCarrier::CreateAirVehicle(routeidx)
// Build an aircraft
{
if (!INSTANCE.use_air) return false;
local road = cRoute.Load(routeidx);
if (!road) return false;
local engineID = cCarrier.GetAirVehicle(routeidx);
if (engineID == -1) { DWarn("Cannot find any aircraft to transport that cargo "+cCargo.GetCargoLabel(road.CargoID),1); return false; }
local srcplace = road.SourceStation.s_Location;
local dstplace = road.TargetStation.s_Location;
local homedepot = road.SourceStation.s_Depot;
local altplace=(road.Twoway && road.VehicleCount > 0 && road.VehicleCount % 2 != 0);
if (road.VehicleType == RouteType.CHOPPER) altplace=true; // chopper don't have a source airport, but a platform
if (altplace) homedepot = road.TargetStation.s_Depot;
if (!cStation.IsDepot(homedepot))
{
homedepot = cRoute.GetDepot(routeidx);
if (!cStation.IsDepot(homedepot)) return false;
}
local price = cEngine.GetPrice(engineID);
cBanker.RaiseFundsBy(price);
local vehID = cEngineLib.CreateVehicle(homedepot, engineID, -1); // force no refit
if (vehID != -1)
{
DInfo("Just brought a new aircraft vehicle: "+cCarrier.GetVehicleName(vehID),0);
INSTANCE.main.carrier.vehicle_cash -= price;
INSTANCE.main.carrier.highcostAircraft = 0;
}
else {
DError("Cannot create the aircraft vehicle "+cEngine.GetName(engineID),2);
return false;
}
// no refit on aircrafts, we endup with only passengers aircraft, and ones that should do mail will stay different
// as their engine is the fastest always
local firstorderflag = null;
local secondorderflag = null;
secondorderflag = AIOrder.OF_NONE;
AIOrder.AppendOrder(vehID, srcplace, secondorderflag);
AIOrder.AppendOrder(vehID, dstplace, secondorderflag);
AIGroup.MoveVehicle(road.GroupID, vehID);
if (altplace) INSTANCE.main.carrier.VehicleOrderSkipCurrent(vehID);
if (!cCarrier.StartVehicle(vehID)) { DError("Cannot start the vehicle: "+cCarrier.GetVehicleName(vehID),2); cCarrier.VehicleSell(vehID, false); return false;}
road.VehicleCount++;
return true;
}
function cCarrier::AircraftIsChopper(vehicle)
// return true if we are a chopper
{
local vehlist = AIEngineList(AIVehicle.VT_AIR);
vehlist.Valuate(AIEngine.GetPlaneType);
vehlist.KeepValue(AIAirport.PT_HELICOPTER);
local vehengine=AIVehicle.GetEngineType(vehicle);
return vehlist.HasItem(vehengine);
}
DictatorAI/build/ 0000777 0001750 0000144 00000000000 12205361010 013172 5 ustar krinn users DictatorAI/build/vehiclebuilder.nut 0000755 0001750 0000144 00000052252 12203275327 016732 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
// generic vehicle building functions
function cCarrier::VehicleGetCargoType(veh)
// return cargo type the vehicle is handling
{
local cargotype=AICargoList();
foreach (cargo, dummy in cargotype)
{
if (AIVehicle.GetCapacity(veh, cargo) > 0) return cargo;
}
}
function cCarrier::VehicleGetProfit(veh)
// add a vehicle to do_profit list, calc its profit and also return it
{
local profit=AIVehicle.GetProfitThisYear(veh);
local oldprofit=0;
if (INSTANCE.main.carrier.do_profit.HasItem(veh)) oldprofit=INSTANCE.main.carrier.do_profit.GetValue(veh);
else INSTANCE.main.carrier.do_profit.AddItem(veh,0);
if (profit > oldprofit) oldprofit=profit - oldprofit;
else oldprofit=oldprofit+profit;
INSTANCE.main.carrier.do_profit.SetValue(veh, oldprofit);
return oldprofit;
}
function cCarrier::CanAddNewVehicle(roadidx, start, max_allow)
// check if we can add another vehicle at the start/end station of that route
{
local chem=cRoute.Load(roadidx);
if (!chem) return 0;
chem.RouteUpdateVehicle();
local thatstation=null;
//local thatentry=null;
local otherstation=null;
if (start) { thatstation=chem.SourceStation; otherstation=chem.TargetStation; }
else { thatstation=chem.TargetStation; otherstation=chem.SourceStation; }
local divisor=0; // hihi what a bad default value to start with
local sellvalid=( (AIDate.GetCurrentDate() - chem.DateVehicleDelete) > 60);
// prevent buy a new vehicle if we sell one less than 60 days before (this isn't affect by replacing/upgrading vehicle)
if (!sellvalid) { max_allow=0; DInfo("Route sold a vehicle not a long time ago",1); return 0; }
local virtualized=cStation.IsStationVirtual(thatstation.s_ID);
local othervirtual=cStation.IsStationVirtual(otherstation.s_ID);
local airportmode="(classic)";
local shared=false;
if (thatstation.s_Owner.Count() > 1) { shared=true; airportmode="(shared)"; }
//if (virtualized) airportmode="(network)";
local airname=thatstation.s_Name+"-> ";
switch (chem.VehicleType)
{
case RouteType.ROAD:
DInfo("Road station "+thatstation.s_Name+" limit "+thatstation.s_VehicleCount+"/"+thatstation.s_VehicleMax,1);
if (thatstation.CanUpgradeStation())
{ // can still upgrade
if (chem.VehicleCount+max_allow > INSTANCE.main.carrier.road_max_onroute)
{ max_allow=(INSTANCE.main.carrier.road_max_onroute-chem.VehicleCount); }
// limit by number of vehicle per route
if (!INSTANCE.use_road) { max_allow=0; }
// limit by vehicle disable (this can happen if we reach max vehicle game settings too
if ( (thatstation.s_VehicleCount+max_allow) > thatstation.s_VehicleMax)
{ // we must upgrade
INSTANCE.main.builder.RoadStationNeedUpgrade(roadidx, start);
local fake=thatstation.CanUpgradeStation(); // to see if upgrade success
}
if (thatstation.s_VehicleCount+max_allow > thatstation.s_VehicleMax)
{ max_allow=thatstation.s_VehicleMax-thatstation.s_VehicleCount; }
// limit by the max the station could handle
}
else { // max size already
if (thatstation.s_VehicleCount+max_allow > thatstation.s_VehicleMax)
{ max_allow=INSTANCE.main.carrier.road_max_onroute-thatstation.s_VehicleCount; }
// limit by the max the station could handle
if (chem.VehicleCount+max_allow > INSTANCE.main.carrier.road_max_onroute)
{ max_allow=INSTANCE.main.carrier.road_max_onroute-chem.VehicleCount; }
// limit by number of vehicle per route
}
break;
case RouteType.RAIL:
if (!INSTANCE.use_train) { max_allow = 0; }
break;
case RouteType.WATER:
if (!INSTANCE.use_boat) { max_allow = 0; }
if (thatstation.s_VehicleCount+max_allow > thatstation.s_VehicleMax)
{ max_allow=thatstation.s_VehicleMax-thatstation.s_VehicleCount; }
break;
case RouteType.AIRNET:
case RouteType.AIRNETMAIL:
if (!INSTANCE.use_air) { max_allow = 0; }
local netlimit = cCarrier.VirtualAirRoute.len() * INSTANCE.main.carrier.airnet_max;
if (max_allow > netlimit) { max_allow = netlimit - vehnumber; }
/* thatstation.CheckAirportLimits(); // force recheck limits
if (thatstation.CanUpgradeStation())
{
INSTANCE.main.builder.AirportNeedUpgrade(thatstation.s_ID);
return 0;
// get out after an upgrade, station could have change place...
}
DInfo(airname+"Limit for that route (network): "+chem.VehicleCount+"/"+INSTANCE.main.carrier.airnet_max*cCarrier.VirtualAirRoute.len(),1);
DInfo(airname+"Limit for that airport (network): "+chem.VehicleCount+"/"+thatstation.s_VehicleMax,1);
if (chem.VehicleCount+max_allow > INSTANCE.main.carrier.airnet_max*cCarrier.VirtualAirRoute.len()) max_allow=(INSTANCE.main.carrier.airnet_max*cCarrier.VirtualAirRoute.len()) - chem.VehicleCount;
if (chem.VehicleCount+max_allow > thatstation.s_VehicleMax) max_allow=thatstation.s_VehicleMax-chem.VehicleCount;*/
break;
case RouteType.CHOPPER:
DInfo(airname+"Limit for that route (choppers): "+chem.VehicleCount+"/4",1);
DInfo(airname+"Limit for that airport "+airportmode+": "+thatstation.s_VehicleMax,1);
if (chem.VehicleCount+max_allow > 4) max_allow=4-chem.VehicleCount;
break;
case RouteType.AIR: // Airport upgrade is not related to number of aircrafts using them
case RouteType.AIRMAIL:
case RouteType.SMALLAIR:
case RouteType.SMALLMAIL:
thatstation.CheckAirportLimits(); // force recheck limits
if (!INSTANCE.use_air) { max_allow = 0; }
if (thatstation.CanUpgradeStation())
{
INSTANCE.main.builder.AirportNeedUpgrade(thatstation.s_ID);
max_allow=0;
}
local limitmax = INSTANCE.main.carrier.air_max;
if (shared)
{
if (thatstation.s_Owner.Count()>0) { limitmax=limitmax / thatstation.s_Owner.Count(); }
if (limitmax < 2) limitmax=2;
}
// if (virtualized) limitmax=4; // only 4 aircrafts when the airport is also in network
/* local dualnetwork=false;
local routemod="(classic)";
if (virtualized && othervirtual)
{
limitmax=2; // no aircrafts at all on that route if both airport are in the network
dualnetwork=true;
routemod="(dual network)";
}*/
DInfo(airname+"Limit for that route "+airportmode+": "+chem.VehicleCount+"/"+limitmax,1);
DInfo(airname+"Limit for that airport "+airportmode+": "+thatstation.s_VehicleCount+"/"+thatstation.s_VehicleMax,1);
if (chem.VehicleCount+max_allow > limitmax) { max_allow=limitmax - chem.VehicleCount; }
// limit by route limit
if (thatstation.s_VehicleCount+max_allow > thatstation.s_VehicleMax) { max_allow=thatstation.s_VehicleMax-thatstation.s_VehicleCount; }
// limit by airport capacity
break;
}
if (max_allow < 0) { max_allow=0; }
return max_allow;
}
function cCarrier::GetVehicle(routeidx)
// return the vehicle we will pickup if we build a vehicle for that route
{
local road=cRoute.Load(routeidx);
if (!road) return null;
switch (road.VehicleType)
{
case RouteType.RAIL:
return INSTANCE.main.carrier.ChooseRailCouple(road.CargoID, road.RailType, road.GetDepot(routeidx));
break;
case RouteType.WATER:
return INSTANCE.main.carrier.GetWaterVehicle(routeidx);
break;
case RouteType.ROAD:
return INSTANCE.main.carrier.GetRoadVehicle(routeidx);
break;
default: // to catch all AIR type
return INSTANCE.main.carrier.GetAirVehicle(routeidx);
break;
}
}
function cCarrier::GetEngineEfficiency(engine, cargoID)
// engine = enginetype to check
// return an index, the smallest = the better of ratio cargo/runningcost+cost of engine
{
local price=cEngine.GetPrice(engine, cargoID);
local capacity=cEngine.GetCapacity(engine, cargoID);
local lifetime=AIEngine.GetMaxAge(engine);
local runningcost=AIEngine.GetRunningCost(engine);
local speed=AIEngine.GetMaxSpeed(engine);
if (capacity==0) return 9999999;
if (price<=0) return 9999999;
local eff=(100000+ (price+(lifetime*runningcost))) / ((capacity*0.9)+speed).tointeger();
return eff;
}
function cCarrier::GetEngineRawEfficiency(engine, cargoID, fast)
// only consider the raw capacity/speed ratio
// engine = enginetype to check
// if fast=true try to get the fastest engine even if capacity is a bit lower than another
// return an index, the smallest = the better of ratio cargo/runningcost+cost of engine
{
local price=cEngine.GetPrice(engine, cargoID);
local capacity=cEngine.GetCapacity(engine, cargoID);
local speed=AIEngine.GetMaxSpeed(engine);
local lifetime=AIEngine.GetMaxAge(engine);
local runningcost=AIEngine.GetRunningCost(engine);
if (capacity<=0) return 9999999;
if (price<=0) return 9999999;
local eff=0;
if (fast) eff=1000000 / ((capacity*0.9)+speed).tointeger();
else eff=1000000-(capacity * speed);
return eff;
}
function cCarrier::GetEngineLocoEfficiency(engine, cargoID, cheap)
// Get a ratio for a loco engine
// if cheap=true return the best ratio the loco have for the best ratio prize/efficiency, if false just the best engine without any costs influence
// return an index, the smallest = the better
{
//cheap = false;
local price=cEngineLib.GetPrice(engine, cargoID);
//print("price = "+price+" normalprice="+AIEngine.GetPrice(engine));
local power=AIEngine.GetPower(engine);
local speed=AIEngine.GetMaxSpeed(engine);
local lifetime=AIEngine.GetMaxAge(engine);
local runningcost=AIEngine.GetRunningCost(engine);
local eff=0;
local rawidx=((power*(0.9*speed)) * 0.01)+1;
//print("rawidx for "+AIEngine.GetName(engine)+" = "+rawidx);
if (cheap) eff=(100000+ (price+(lifetime*runningcost))) / rawidx ;
else eff=(200000 - rawidx);
return eff.tointeger();
}
function cCarrier::GetEngineWagonEfficiency(engine, cargoID)
// Get the ratio for a wagon engine
// return an index, the bigger the better
{
local capacity = cEngine.GetCapacity(engine, cargoID);
local speed = AIEngine.GetMaxSpeed(engine);
local idx = -1;
if (AIGameSettings.GetValue("wagon_speed_limits") == 1)
{
if (speed == 0) speed = cEngineLib.GetTrainMaximumSpeed();
idx = speed * capacity;
}
else idx = capacity;
return idx;
}
function cCarrier::CheckOneVehicleOrGroup(vehID, doGroup)
// Add a vehicle to the maintenance pool
// vehID: the vehicleID to check
// doGroup: if true, we will add all the vehicles that belong to the vehicleID group
{
if (!AIVehicle.IsValidVehicle(vehID)) return false;
local vehList=AIList();
local vehGroup=AIVehicle.GetGroupID(vehID);
if (doGroup) vehList.AddList(AIVehicleList_Group(vehGroup));
if (vehList.IsEmpty()) vehList.AddItem(vehID,0);
foreach (vehicle, dummy in vehList)
cCarrier.MaintenancePool.push(vehicle); // allow dup vehicleID in list, this will get clear by cCarrier.VehicleMaintenance()
}
function cCarrier::CheckOneVehicleOfGroup(doGroup)
// Add one vehicle of each vehicle groups we own to maintenance check
// doGroup: true to also do the whole group add, this mean all vehicles we own
{
local allgroup=AIGroupList();
foreach (groupID, dummy in allgroup)
{
local vehlist=AIVehicleList_Group(groupID);
vehlist.Valuate(AIVehicle.GetAge);
vehlist.Sort(AIList.SORT_BY_VALUE,false);
if (!vehlist.IsEmpty()) cCarrier.CheckOneVehicleOrGroup(vehlist.Begin(),doGroup);
local pause = cLooper();
}
}
function cCarrier::VehicleFilterRoad(vehlist, object)
{
cEngineLib.EngineFilter(vehlist, object.cargo_id, object.engine_roadtype, -1, false);
vehlist.Valuate(AIEngine.GetPrice);
vehlist.RemoveValue(0); // remove towncars toys
vehlist.Valuate(AIEngine.IsArticulated);
vehlist.KeepValue(0);
vehlist.Valuate(cEngineLib.GetCapacity, object.cargo_id);
vehlist.RemoveBelowValue(8); // clean out too small dumb vehicle size
if (INSTANCE.main.bank.unleash_road) vehlist.Valuate(cCarrier.GetEngineRawEfficiency, object.cargo_id, true);
else vehlist.Valuate(cCarrier.GetEngineEfficiency, object.cargo_id);
vehlist.Sort(AIList.SORT_BY_VALUE,true);
if (!vehlist.IsEmpty()) cEngine.EngineIsTop(vehlist.Begin(), object.cargo_id, true); // set top engine for trucks
}
function cCarrier::VehicleFilterWater(vehlist, object)
{
cEngineLib.EngineFilter(vehlist, object.cargo_id, -1, -1, false);
vehlist.Valuate(AIEngine.GetPrice);
vehlist.RemoveValue(0); // remove towncars toys
vehlist.Valuate(cEngineLib.GetCapacity, object.cargo_id);
vehlist.RemoveBelowValue(8); // clean out too small dumb vehicle size
if (INSTANCE.main.bank.unleash_road) vehlist.Valuate(cCarrier.GetEngineRawEfficiency, object.cargo_id, true);
else vehlist.Valuate(cCarrier.GetEngineEfficiency, object.cargo_id);
vehlist.Sort(AIList.SORT_BY_VALUE,true);
if (!vehlist.IsEmpty()) cEngine.EngineIsTop(vehlist.Begin(), object.cargo_id, true); // set top engine for trucks
}
function cCarrier::VehicleFilterAir(vehlist, object)
{
local passCargo = cCargo.GetPassengerCargo();
vehlist.Valuate(AIEngine.IsBuildable);
vehlist.KeepValue(1);
vehlist.Valuate(AIEngine.GetMaxSpeed);
vehlist.KeepAboveValue(45); // some newgrf use weird unplayable aircrafts (for our distance usage)
vehlist.Valuate(AIEngine.GetMaximumOrderDistance);
vehlist.KeepValue(0); // Add for newGRF distance limit, for now only allow no limit engine
local special = 0;
local limitsmall = false;
local fastengine = false;
if (object.bypass == 20)
{
special = AircraftType.EFFICIENT;
limitsmall = true;
}
switch (object.bypass)
{
case AircraftType.EFFICIENT: // top efficient aircraft for passenger and top speed (not efficient) for mail
// top efficient aircraft is generally the same as top capacity/efficient one
vehlist.Valuate(AIEngine.GetMaxSpeed);
vehlist.RemoveBelowValue(65); // remove too dumb aircraft 65=~250km/h
vehlist.Valuate(cEngine.GetCapacity, passCargo);
vehlist.RemoveBelowValue(30);
if (limitsmall) // small ones
{
vehlist.Valuate(AIEngine.GetPlaneType);
vehlist.KeepValue(AIAirport.PT_SMALL_PLANE);
special = RouteType.SMALLAIR;
}
else special=RouteType.AIR;
if (AICargo.GetTownEffect(object.cargo_id) == AICargo.TE_MAIL)
{ // mail/fast ones
vehlist.Valuate(AIEngine.GetMaxSpeed);
special++; // add one to fall on mail: AIRMAIL OR SMALLMAIL
vehlist.Sort(AIList.SORT_BY_VALUE,false);
vehlist.KeepTop(5); // best fastest engine out of the 5 top fast one
}
else { // passengers
vehlist.Valuate(AIEngine.GetCapacity);
vehlist.Sort(AIList.SORT_BY_VALUE,false);
vehlist.KeepTop(5);
}
vehlist.Valuate(cCarrier.GetEngineEfficiency, passCargo); // passenger/big ones
vehlist.Sort(AIList.SORT_BY_VALUE,true);
break;
case AircraftType.BEST:
special = RouteType.AIRNET;
if (AICargo.GetTownEffect(object.cargo_id) == AICargo.TE_MAIL) // fast aircraft
{
special++; // mail: AIRNETMAIL
fastengine=true;
}
vehlist.Valuate(cCarrier.GetEngineRawEfficiency, passCargo, fastengine); // keep top raw efficiency out of remain ones
vehlist.Sort(AIList.SORT_BY_VALUE,true); // for fast aircrafts only 5 choices, but big aircrafts have plenty choices
break;
case AircraftType.CHOPPER: // top efficient chopper
vehlist.Valuate(AIEngine.GetPlaneType);
vehlist.KeepValue(AIAirport.PT_HELICOPTER);
vehlist.Valuate(cCarrier.GetEngineEfficiency, passCargo);
vehlist.Sort(AIList.SORT_BY_VALUE,true);
special = RouteType.CHOPPER;
break;
}
if (!vehlist.IsEmpty()) cEngine.EngineIsTop(vehlist.Begin(), special, true); // set top engine for aircraft
//if (!vehlist.IsEmpty()) print("aircraft="+cEngine.GetName(vehlist.Begin())+" r_dist="+distance+" r_distSQ="+(distance*distance)+" e_dist="+AIEngine.GetMaximumOrderDistance(vehlist.Begin()));
}
function cCarrier::VehicleFilterTrain(vehlist, object)
{
cEngineLib.EngineFilter(vehlist, object.cargo_id, -1, object.engine_id, object.bypass);
// force roadtype to -1 to prevent filtering by railtype
if (AIEngine.IsWagon(vehlist.Begin()))
{
vehlist.Valuate(cCarrier.GetEngineWagonEfficiency, object.cargo_id);
vehlist.Sort(AIList.SORT_BY_VALUE, false);
}
else {
vehlist.Valuate(cCarrier.GetEngineLocoEfficiency, object.cargo_id, !INSTANCE.main.bank.unleash_road);
vehlist.Sort(AIList.SORT_BY_VALUE, true);
if (!vehlist.IsEmpty()) cEngine.RailTypeIsTop(vehlist.Begin(), object.cargo_id, true);
// before railtype filtering, add this engine as topengine using any railtype
if (object.engine_roadtype != -1)
{
vehlist.Valuate(AIEngine.HasPowerOnRail, object.engine_roadtype);
vehlist.KeepValue(1);
vehlist.Valuate(cCarrier.GetEngineLocoEfficiency, object.cargo_id, !INSTANCE.main.bank.unleash_road);
vehlist.Sort(AIList.SORT_BY_VALUE, true);
}
if (!vehlist.IsEmpty()) cEngine.EngineIsTop(vehlist.Begin(), object.cargo_id, true); // set top engine for trains
}
}
function cCarrier::RouteNeedVehicle(gid, amount)
// store the vehicle need by all routes
{
if (amount == 0) return;
if (!INSTANCE.main.carrier.vehicle_wishlist.HasItem(gid)) { INSTANCE.main.carrier.vehicle_wishlist.AddItem(gid, 0); }
INSTANCE.main.carrier.vehicle_wishlist.SetValue(gid, amount);
}
function cCarrier::PriorityGroup(gid)
{
local type = AIGroup.GetVehicleType(gid);
if (type == AIVehicle.VT_RAIL) return 100;
if (type == AIVehicle.VT_ROAD) return 30;
if (type == AIVehicle.VT_AIR) return 60;
return 0;
}
function cCarrier::Lower_VehicleWish(gid, amount)
// dec the wish list for that gid
{
if (!INSTANCE.main.carrier.vehicle_wishlist.HasItem(gid)) return;
local value = INSTANCE.main.carrier.vehicle_wishlist.GetValue(gid);
local x = 0;
if (value > 1000) { x = 1000; }
value -= amount;
if (value <= x) { INSTANCE.main.carrier.vehicle_wishlist.RemoveItem(gid); return; }
INSTANCE.main.carrier.vehicle_wishlist.SetValue(gid, value);
}
function cCarrier::Process_VehicleWish()
// buy vehicle need by routes
{
INSTANCE.main.carrier.highcostAircraft = 0;
INSTANCE.main.carrier.highcostTrain = 0;
local uid, engine, gtype;
local cleanList = AIList();
cleanList.AddList(INSTANCE.main.carrier.vehicle_wishlist);
cleanList.Valuate(AIGroup.IsValidGroup);
foreach (gid, exist in cleanList)
{
if (exist == 1) { continue; }
INSTANCE.main.carrier.vehicle_wishlist.RemoveItem(gid);
}
if (INSTANCE.main.carrier.vehicle_wishlist.IsEmpty()) { INSTANCE.main.carrier.vehicle_cash = 0; return; }
cleanList.KeepValue(1);
cleanList.Valuate(cCarrier.PriorityGroup);
cleanList.Sort(AIList.SORT_BY_VALUE, AIList.SORT_DESCENDING);
DInfo(cleanList.Count()+" queries to buy vehicle waiting",1);
if (INSTANCE.debug)
{
foreach (gid, amount in cleanList)
{
local uid = cRoute.GroupIndexer.GetValue(gid);
local r = cRoute.Load(uid);
if (!r) { continue; }
DInfo(r.Name+" Need: "+INSTANCE.main.carrier.vehicle_wishlist.GetValue(gid),1);
}
}
local amount = 0;
foreach (gid, _ in cleanList)
{
uid = cRoute.GroupIndexer.GetValue(gid);
local r = cRoute.Load(uid);
if (!r || r.Status != RouteStatus.WORKING || !AIGroup.IsValidGroup(gid))
{
INSTANCE.main.carrier.vehicle_wishlist.RemoveItem(uid);
continue;
}
gtype = AIGroup.GetVehicleType(gid);
engine = cCarrier.GetVehicle(uid);
amount = INSTANCE.main.carrier.vehicle_wishlist.GetValue(gid);
if (engine == null || engine == -1) { continue; }
if (typeof(engine) == "array")
{
if (engine[0] == -1) { continue; }
engine = engine[1];
}
local price = cEngineLib.GetPrice(engine);
local aircraft = false;
if (amount >= 1000)
{
amount -= 1000;
}
else {
INSTANCE.main.carrier.vehicle_cash += (price * amount);
INSTANCE.main.carrier.vehicle_wishlist.SetValue(gid, 1000 + amount);
}
if (amount == 0) { INSTANCE.main.carrier.vehicle_wishlist.RemoveItem(gid); continue; }
local creation = null;
switch (gtype)
{
case AIVehicle.VT_AIR:
creation = cCarrier.CreateAirVehicle;
aircraft = true;
break;
case AIVehicle.VT_RAIL:
if (cCarrier.IsTrainRouteBusy(uid)) { continue; }
cCarrier.AddWagon(uid, amount);
continue;
case AIVehicle.VT_ROAD:
creation = cCarrier.CreateRoadVehicle;
break;
case AIVehicle.VT_WATER:
creation = cCarrier.CreateWaterVehicle;
break;
}
if (creation == null) continue;
for (local z = 0; z < amount; z++)
{
if (cBanker.CanBuyThat(price))
{
if (creation(uid))
{
cCarrier.Lower_VehicleWish(gid, 1);
}
}
else if (aircraft && INSTANCE.main.carrier.highcostAircraft < price)
{
INSTANCE.main.carrier.highcostAircraft = price;
}
}
}
}
DictatorAI/build/track.nut 0000755 0001750 0000144 00000022311 12205361007 015032 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cTrack extends cClass
{
//constructor() {}
}
function cTrack::SetRailType(rtype = -1)
/** @brief Set the current railtype
*
* @param rtype The railtype need, if -1 return the current best railtype
* @return True if railtype was set
*
*/
{
if (rtype == -1)
{
local railtypes = AIRailTypeList();
if (railtypes.IsEmpty()) { DError("There's no railtype avaiable !",1); return false; }
rtype = cEngineLib.GetBestRailType();
}
if (!AIRail.IsRailTypeAvailable(rtype)) { DError("Railtype "+rtype+" is not available !",1); return false; }
AIRail.SetCurrentRailType(rtype);
return true; // assuming it has done it
}
function cTrack::BuildTrack_Road(tilefrom, tileto, stationID = -1, full = false)
/** @brief Build a road track from tilefrom to tileto
*
* @param tilefrom The source tile
* @param tileto The target tile
* @param full If true use AIRoad.BuildRoadFull else we use AIRoad.BuildRoad
* @param the stationID to assign the track too, set the track as compatible type with the station roadtype
* @return true if track was built or if a compatible track exist already (must have stationID set to find that)
*
*/
{
if (stationID != -1)
{
local roadtype = AIRoad.ROADTYPE_ROAD;
local loc = AIStation.GetLocation(stationID);
if (AIRoad.HasRoadType(loc, AIRoad.ROADTYPE_TRAM)) { roadtype = AIRoad.ROADTYPE_TRAM; }
AIRoad.SetCurrentRoadType(roadtype);
}
local api = AIRoad.BuildRoad;
if (full) { api = AIRoad.BuildRoadFull; }
local success = cError.ForceAction(api, tilefrom, tileto);
local tiles = AIList();
tiles.AddItem(tileto, 0);
tiles.AddItem(tilefrom, 0);
if (success && stationID != -1) { cStation.StationClaimTile(tiles, stationID, -1); }
return success;
}
function cTrack::RoadCleaner(targetTile, stationID = -1)
/** @brief Clean a tile or AIList of tiles from any roadtype structure, except station
*
* @param targetTile A tile or an AIList of tiles to clean
* @param stationID the stationID
* @return true if all tiles have been removed. targetTile: an AIList with tiles that were removed in it.
*
*/
{
local many=AIList();
if (cMisc.IsAIList(targetTile)) { many.AddList(targetTile); targetTile.Clear(); }
else { many.AddItem(targetTile, 0); targetTile = AIList(); }
if (many.IsEmpty()) { return true; }
if (!DictatorAI.GetSetting("keep_road")) { return false; }
local success = true;
local voisin=[AIMap.GetTileIndex(0,1), AIMap.GetTileIndex(0,-1), AIMap.GetTileIndex(1,0), AIMap.GetTileIndex(-1,0)]; // SE, NW, SW, NE
foreach (tile, dummy in many)
{
local looper = cLooper();
cDebug.PutSign(tile,"Z");
if (AITile.GetOwner(tile) != AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)) { success = false; continue; }
if (AIRoad.IsRoadStationTile(tile)) { success = false; continue; } // protect station
if (AIRoad.IsRoadDepotTile(tile))
{
if (cTrack.DestroyDepot(tile, -1)) { targetTile.AddItem(tile, 0); }
else { success= false; }
continue;
}
if (AITile.HasTransportType(tile, AITile.TRANSPORT_ROAD) && (AIBridge.IsBridgeTile(tile) || AITunnel.IsTunnelTile(tile)) )
{
if (cError.ForceAction(AITile.DemolishTile, tile)) { targetTile.AddItem(tile, 0); }
else { success = false; }
continue;
}
foreach (near in voisin)
{
cError.ForceAction(AIRoad.RemoveRoadFull, tile, tile + near);
}
if (AIRoad.IsRoadTile(tile)) { success = false; }
else { targetTile.AddItem(tile, 0); }
}
if (stationID != -1) { cStation.StationReleaseTile(many, stationID); }
return success;
}
function cTrack::DestroyDepot(tile, stationID = -1)
/** @brief Remove a depot at tile, sold vehicles in it that might prevent us doing it
*
* @param tile The depot tile location
* @param stationID the station affect by the operation
* @return true if depot was removed
*
*/
{
if (!cStation.IsDepot(tile)) { return false; }
if (!cCarrier.FreeDepotOfVehicle(tile)) { return false; }
if (!cError.ForceAction(AITile.DemolishTile, tile)) { return false; }
if (stationID != -1) { cStation.StationReleaseTile(tile, stationID); }
return true;
}
function cTrack::DropRailHere(railneed, pos, stationID = -1, useEntry = -1)
/** @brief Add a railtrack at position
*
* @param railneed The rail track to build. If < 0 (railneed+1) the rail track to clear.
* @param pos The tile to work at
* @param stationID the station to assign the track with
* @param useEntry The entry/exit of the stationID
* @return True on success
*
*/
{
local lasterr = -1;
if (railneed < 0)
{
if (!AIRail.IsRailTile(pos)) { return true; }
else { return cError.ForceAction(AIRail.RemoveRailTrack, pos, abs(railneed+1)); }
}
local count = 100;
local success = AIRail.BuildRailTrack(pos, railneed);
while (!success && count > 0)
{
success = AIRail.BuildRailTrack(pos, railneed);
count--;
lasterr=AIError.GetLastError();
switch (lasterr)
{
case AIError.ERR_AREA_NOT_CLEAR:
if (!cTileTools.DemolishTile(pos)) { return false; }
break;
case AIError.ERR_VEHICLE_IN_THE_WAY:
AIController.Sleep(10);
break;
case AIError.ERR_ALREADY_BUILT:
return true;
default:
DError("Cannot build rail track at "+pos,1);
return false;
}
}
if (success && stationID != -1) { cStation.StationClaimTile(pos, stationID, useEntry); }
return true;
}
function cTrack::RailCleaner(targetTile, stationID = -1)
/** @brief Clean a tile from any rails structure, except station
*
* @param targetTile A tile or an AIList of tiles to clean
* @param stationID the stationID
* @return true if all tiles have been removed. targetTile: an AIList with tiles that were removed in it.
*
*/
{
local many=AIList();
local success = true;
if (cMisc.IsAIList(targetTile)) { many.AddList(targetTile); targetTile.Clear(); }
else { many.AddItem(targetTile, 0); targetTile = AIList(); }
if (many.IsEmpty()) { return true; }
local voisin=[AIMap.GetTileIndex(0,1), AIMap.GetTileIndex(0,-1), AIMap.GetTileIndex(1,0), AIMap.GetTileIndex(-1,0)]; // SE, NW, SW, NE
local trackMap=AIList();
local seek=null;
trackMap.AddItem(AIRail.RAILTRACK_NE_SW, 0);
trackMap.AddItem(AIRail.RAILTRACK_NW_SE, 0);
trackMap.AddItem(AIRail.RAILTRACK_NW_NE, 0);
trackMap.AddItem(AIRail.RAILTRACK_SW_SE, 0);
trackMap.AddItem(AIRail.RAILTRACK_NW_SW, 0);
trackMap.AddItem(AIRail.RAILTRACK_NE_SE, 0);
foreach (tile, dummy in many)
{
cDebug.PutSign(tile,"Z");
local loop = cLooper();
if (AIRail.IsRailStationTile(tile)) { success = false; continue; } // protect station
if (AIRail.IsRailDepotTile(tile))
{
if (cTrack.DestroyDepot(tile, -1)) { targetTile.AddItem(tile, 0); }
else { success = false; }
continue;
}
if (AITile.HasTransportType(tile, AITile.TRANSPORT_RAIL) && (AIBridge.IsBridgeTile(tile) || AITunnel.IsTunnelTile(tile)) )
{
if (cError.ForceAction(AITile.DemolishTile, tile)) { targetTile.AddItem(tile, 0); }
else { success = false; }
continue;
}
foreach (near in voisin)
{
if (AIRail.GetSignalType(tile, tile+near) != AIRail.SIGNALTYPE_NONE) { cError.ForceAction(AIRail.RemoveSignal, tile, tile+near); }
}
seek=AIRail.GetRailTracks(tile);
if (seek != 255)
{
foreach (railtype, dummy in trackMap)
if ((seek & railtype) == railtype) { cError.ForceAction(AIRail.RemoveRailTrack, tile, railtype); }
}
if (AIRail.GetRailTracks(tile) == 255) { targetTile.AddItem(tile, 0); }
else { success = false; }
}
if (stationID != -1) { cStation.StationReleaseTile(many, stationID); }
return success;
}
function cTrack::StationKillRailDepot(tile, stationID = -1)
// Just because we need to remove the depot at tile, and retry to make sure we can
{
if (!AIRail.IsRailDepotTile(tile)) { return; }
local vehlist=AIVehicleList();
vehlist.Valuate(AIVehicle.GetState);
vehlist.KeepValue(AIVehicle.VS_IN_DEPOT);
vehlist.Valuate(AIVehicle.GetLocation);
vehlist.KeepValue(tile);
if (!vehlist.IsEmpty()) { DInfo("Restarting trains at depot "+tile+" so we can remove it",1); }
foreach (veh, dummy in vehlist)
{
DInfo("Starting "+cCarrier.GetVehicleName(veh)+"...",0);
cTrain.SetDepotVisit(veh);
cCarrier.StartVehicle(veh);
AIController.Sleep(40);
}
local removed = cError.ForceAction(AITile.DemolishTile, tile);
if (!removed) { return false; }
if (stationID == -1) { return true; }
cStation.StationReleaseTile(tile, stationID);
}
function cTrack::ConvertRailType(tile, newrt)
// return 1 ok, -1 not ok, 0 not ok but can retry
{
if (!AIMap.IsValidTile) { return 1; }
if (tile == -1) { return 1; }
if (AIRail.GetRailType(tile) == newrt) { return 1; }
if (!cError.ForceAction(AIRail.ConvertRailType, tile, tile, newrt))
{
local error = AIError.GetLastError();
if (error == AIError.ERR_NONE) { return 1; }
if (error == AIError.ERR_NOT_ENOUGH_CASH) { return 0; }
return -1;
}
return 1;
}
DictatorAI/build/builder.nut 0000755 0001750 0000144 00000046502 12204106611 015361 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cBuilder::StationIsAccepting(stationid)
// add that station to the station_drop list
{
if (!INSTANCE.main.builder.station_drop.HasItem(stationid)) INSTANCE.main.builder.station_take.AddItem(stationid, 1);
}
function cBuilder::StationIsProviding(stationid)
// add that station to the station_take list
{
if (!INSTANCE.main.builder.station_take.HasItem(stationid)) INSTANCE.main.builder.station_take.AddItem(stationid, 1);
}
function cBuilder::ValidateLocation(location, direction, width, depth)
// check a rectangle of 5 length x size width for construction
// true if we are good to go, false on error
{
local tiletester=AITileList();
local checker=null;
switch (direction)
{
case AIRail.RAILTRACK_NE_SW:
// gauche/droite
checker = AIMap.GetTileIndex(depth,width);
break;
case AIRail.RAILTRACK_NW_SE:
// haut/bas
checker = AIMap.GetTileIndex(width,depth);
break;
}
tiletester.AddRectangle(location, location+checker);
local tile=null;
local weare=AICompany.ResolveCompanyID(AICompany.COMPANY_SELF);
// we will demolish anything that can prevent us from building, except if that thing is own by us
// this way we clean the path but still keep our previous tracks on, in case anything goes wrong
// we will still have a valid station
foreach (i, dummy in tiletester)
{
tile=AITile.GetOwner(i);
if (tile == weare) continue; // tile is ok, it's one of our tile
DInfo(tile+" my company "+weare,2);
// Watchout for roadtile from company X crossing our rail
tile=cTileTools.DemolishTile(i); // can we delete that tile ? Ignore result, we will care about it on next try
tile=cTileTools.DemolishTile(i); // demolish it twice, this should remove the crossing roads case
if (!cError.IsCriticalError()) continue;
else { return false; }
}
return true;
}
function cBuilder::GetDirection(tilefrom, tileto)
// SimpleAI code
{
local distx = AIMap.GetTileX(tileto) - AIMap.GetTileX(tilefrom);
local disty = AIMap.GetTileY(tileto) - AIMap.GetTileY(tilefrom);
local ret = 0;
if (abs(distx) > abs(disty)) {
ret = 2;
disty = distx;
}
if (disty > 0) {ret = ret + 1}
return ret;
}
function cBuilder::BuildStation(start)
// Build start station, reroute to the correct station builder depending on the road type to build
{
local success=false;
switch (INSTANCE.main.route.VehicleType)
{
case AIVehicle.VT_ROAD:
success=INSTANCE.main.builder.BuildRoadStation(start);
break;
case AIVehicle.VT_RAIL:
success=INSTANCE.main.builder.BuildTrainStation(start);
break;
case AIVehicle.VT_WATER:
success=INSTANCE.main.builder.BuildWaterStation(start);
break;
case RouteType.AIR:
case RouteType.AIRMAIL:
case RouteType.AIRNET:
case RouteType.AIRNETMAIL:
case RouteType.SMALLAIR:
case RouteType.SMALLMAIL:
case RouteType.CHOPPER:
success=INSTANCE.main.builder.BuildAirStation(start);
// we get the stationID return, but builder expect it to be in SourceStation or TargetStation to claim it
if (success != -1)
{
if (start) INSTANCE.main.route.SourceStation = success;
else INSTANCE.main.route.TargetStation = success;
success = true;
}
else success=false;
break;
}
return success;
}
function cBuilder::BuildRoadByType()
// build the road, reroute to correct function depending on road type
// for all except trains, src & dst = station location of start & destination station
{
local success=false;
switch (INSTANCE.main.route.VehicleType)
{
case AIVehicle.VT_ROAD:
DInfo("Calling road pathfinder: from "+INSTANCE.main.route.SourceStation.s_Name+" to "+INSTANCE.main.route.TargetStation.s_Name,2);
local fromsrc=INSTANCE.main.route.SourceStation.GetRoadStationEntry();
local todst=INSTANCE.main.route.TargetStation.GetRoadStationEntry();
if (!INSTANCE.main.route.Twoway && INSTANCE.main.builder.RoadRunner(fromsrc, todst, AIVehicle.VT_ROAD, INSTANCE.main.route.Distance)) return true;
INSTANCE.main.route.Twoway = true; // mark it so roadrunner won't be run on next try
local result = cPathfinder.GetStatus(fromsrc, todst, INSTANCE.main.route.SourceStation.s_ID);
cError.ClearError();
if (result == -1) { cError.RaiseError(); cPathfinder.CloseTask(fromsrc, todst); return false;}
if (result == 2) { cPathfinder.CloseTask(fromsrc, todst); return true; }
return false;
case AIVehicle.VT_RAIL:
success=INSTANCE.main.builder.CreateStationsConnection(INSTANCE.main.route.SourceStation.s_ID, INSTANCE.main.route.TargetStation.s_ID);
return success;
case AIVehicle.VT_WATER:
local src_front= cBuilder.GetDockFrontTile(INSTANCE.main.route.SourceStation.s_Location);
local dst_front= cBuilder.GetDockFrontTile(INSTANCE.main.route.TargetStation.s_Location);
success = cBuilder.RoadRunner(src_front, dst_front, AIVehicle.VT_WATER);
if (!success) { cError.RaiseError(); }
return success;
default:
return true;
break;
}
return success;
}
function cBuilder::FindCompatibleStationExistForAllCases(start, stationID)
// Find if we have a station that can be re-use for building_route
// compare start(source/target) station we need vs stationID
// return true if compatible
{
local compare=cStation.Load(stationID);
if (!compare) { return false; }
local sourcevalid = cMisc.ValidInstance(INSTANCE.main.route.SourceStation);
local targetvalid = cMisc.ValidInstance(INSTANCE.main.route.TargetStation);
if (sourcevalid && compare.s_ID == INSTANCE.main.route.SourceStation.s_ID) return false;
if (targetvalid && compare.s_ID == INSTANCE.main.route.TargetStation.s_ID) return false;
// bad we are comparing the same station with itself
DInfo("We are comparing with station #"+compare.s_Name,2);
// find if station will accept our cargo
local handling=true;
if (start)
{
if (!compare.IsCargoProduce(INSTANCE.main.route.CargoID))
{
DInfo("Station "+compare.s_Name+" doesn't produce "+cCargo.GetCargoLabel(INSTANCE.main.route.CargoID),2);
handling=false;
}
}
else {
if (!compare.IsCargoAccept(INSTANCE.main.route.CargoID))
{
DInfo("Station "+compare.s_Name+" doesn't accept "+cCargo.GetCargoLabel(INSTANCE.main.route.CargoID),2);
handling=false;
}
}
if (!handling)
{
DInfo("Station "+compare.s_Name+" refuse "+cCargo.GetCargoLabel(INSTANCE.main.route.CargoID),2);
return false;
}
// here stations are compatibles, but still do that station is within our original station area ?
DInfo(" Checking if "+compare.s_Name+" is within area of our industry/town",2);
local tilecheck = null;
local goal=null;
local istown=false;
if (start) { istown=INSTANCE.main.route.SourceProcess.IsTown; goal=INSTANCE.main.route.SourceProcess.ID; }
else { istown=INSTANCE.main.route.TargetProcess.IsTown; goal=INSTANCE.main.route.TargetProcess.ID; }
if (istown)
{ // check if the station is also influencing our town
tilecheck=cTileTools.StationIsWithinTownInfluence(compare.s_ID, goal);
if (!tilecheck)
{
DInfo(" Station "+compare.s_Name+" is outside "+cProcess.GetProcessName(goal, true)+" influence",2);
return false;
}
}
else { // check the station is within our industry
if (start) tilecheck=AITileList_IndustryProducing(goal, compare.s_Radius);
else tilecheck=AITileList_IndustryAccepting(goal, compare.s_Radius);
// if the station location is in that list, the station touch the industry, nice
local touching = false;
foreach (position, dummy in compare.s_Tiles)
{
if (tilecheck.HasItem(position)) { touching = true; break; }
}
if (touching)
{ DInfo(" Station "+compare.s_Name+" is within range of "+cProcess.GetProcessName(goal, false),2); }
else {
{ DInfo(" Station "+compare.s_Name+" is outside range of "+cProcess.GetProcessName(goal, false),2); }
return false;
}
}
return true;
}
function cBuilder::FindCompatibleStationExists()
// Find if we already have a station on a place
// if compatible, we could link to use that station too
{
// find source station compatible
if (INSTANCE.main.route.StationType == null) return false;
local sList=AIStationList(INSTANCE.main.route.StationType);
DInfo("Looking for a compatible station sList="+sList.Count(),2);
INSTANCE.main.builder.DumpRoute();
local source_success=false;
local target_success=false;
if (!sList.IsEmpty())
{
foreach (stations_check, dummy in sList)
{
source_success=INSTANCE.main.builder.FindCompatibleStationExistForAllCases(true, stations_check);
if (source_success)
{
INSTANCE.main.route.SourceStation = stations_check;
DInfo("Found a compatible station for the source station",1);
break;
}
}
foreach (stations_check, dummy in sList)
{
target_success=INSTANCE.main.builder.FindCompatibleStationExistForAllCases(false, stations_check);
if (target_success)
{
INSTANCE.main.route.TargetStation = stations_check;
DInfo("Found a compatible station for the target station",1);
break;
}
}
}
local allnew=false;
if (INSTANCE.main.route.StationType == AIStation.STATION_TRAIN) // the train special case
{
if (source_success && target_success)
{
local chk_src=cStation.Load(INSTANCE.main.route.SourceStation);
local chk_dst=cStation.Load(INSTANCE.main.route.TargetStation);
local chk_valid=false;
foreach (owns, dummy in chk_src.s_Owner)
if (chk_dst.s_Owner.HasItem(owns)) { chk_valid=true; break; }
allnew=!chk_valid;
}
else allnew=true;
}
if (allnew) { INSTANCE.main.route.SourceStation=null; INSTANCE.main.route.TargetStation=null; source_success=false; target_success=false; } // make sure we create new ones
if (!source_success) DInfo("Failure, creating a new station for our source station.",1);
if (!target_success) DInfo("Failure, creating a new station for our destination station.",1);
}
function cBuilder::TryBuildThatRoute()
// advance the route construction
{
local success=false;
DInfo("Route "+INSTANCE.main.route.Name,1);
DInfo("Status:"+INSTANCE.main.route.Status,1);
// not using switch/case so we can advance steps in one pass
switch (INSTANCE.main.route.VehicleType)
{
case RouteType.RAIL:
if (INSTANCE.main.route.RailType == -1)
{
local trainspec = INSTANCE.main.carrier.ChooseRailCouple(INSTANCE.main.route.CargoID, -1);
if (trainspec[0] == -1) { success = false; }
else { success = true; }
if (success) { INSTANCE.main.route.RailType = cEngineLib.GetBestRailType(trainspec[0]); }
}
else {
local trainspec = INSTANCE.main.carrier.ChooseRailCouple(INSTANCE.main.route.CargoID, INSTANCE.main.route.RailType);
// must be sure one exist, as reusing a station could have change the railtype to use
if (trainspec[0] == -1) { success = false; }
else { success = true; }
}
DInfo("Building using railtype "+AIRail.GetName(INSTANCE.main.route.RailType),2);
cTrack.SetRailType(INSTANCE.main.route.RailType);
break;
case RouteType.ROAD:
success = cCarrier.GetRoadVehicle(null, INSTANCE.main.route.CargoID);
success = (success != -1);
break;
case RouteType.WATER:
success = cCarrier.GetWaterVehicle(null, INSTANCE.main.route.CargoID);
success = (success != -1);
break;
case RouteType.AIR:
case RouteType.AIRMAIL:
case RouteType.AIRNET:
case RouteType.AIRNETMAIL:
case RouteType.SMALLAIR:
case RouteType.SMALLMAIL:
case RouteType.CHOPPER:
local modele=AircraftType.EFFICIENT;
if (!INSTANCE.main.route.SourceProcess.IsTown) modele=AircraftType.CHOPPER;
success= cCarrier.GetAirVehicle(null, INSTANCE.main.route.CargoID, modele);
success = (success != -1);
break;
}
if (!success)
{
DWarn("There's no vehicle we could use to carry that cargo: "+cCargo.GetCargoLabel(INSTANCE.main.route.CargoID),2);
INSTANCE.main.route.Status = RouteStatus.DEAD;
}
else { if (INSTANCE.main.route.Status==0) INSTANCE.main.route.Status=1; } // advance to next phase
if (INSTANCE.main.route.Status==1)
{
INSTANCE.main.builder.FindCompatibleStationExists();
if (cError.IsCriticalError()) // we could get an error when checking to upgrade station
{
if (cError.IsError())
{
cError.ClearError(); // unset it and keep going
}
else { // reason is not critical, lacking funds...
INSTANCE.buildDelay=2;
return false; // let's get out, so we still have a chance to upgrade the station & find its compatibility
}
}
INSTANCE.main.route.Status=2;
}
if (INSTANCE.main.route.Status==2) // change to add check against station is valid
{
if (INSTANCE.main.route.SourceStation == null)
{
if (INSTANCE.main.route.VehicleType == RouteType.RAIL) { cTrack.SetRailType(INSTANCE.main.route.RailType); }
if (INSTANCE.main.route.SourceProcess.IsTown && AITown.GetRating(INSTANCE.main.route.SourceProcess.ID, AICompany.COMPANY_SELF) < AITown.TOWN_RATING_POOR) { cTileTools.SeduceTown(INSTANCE.main.route.SourceProcess.ID); }
success = INSTANCE.main.builder.BuildStation(true);
if (!success && cError.IsError()) INSTANCE.main.route.SourceProcess.ZeroProcess();
}
else {
success=true;
DInfo("Source station is already built, we're reusing an existing one",0);
}
if (success)
{ // attach the new station object to the route, stationID of the new station is hold in SourceStation
INSTANCE.main.route.SourceStation = cStation.Load(INSTANCE.main.route.SourceStation);
if (!INSTANCE.main.route.SourceStation) { cError.RaiseError(); success= false; }
else {
INSTANCE.main.route.SourceStation.OwnerClaimStation(INSTANCE.main.route.UID);
if (INSTANCE.main.route.VehicleType == RouteType.RAIL) { INSTANCE.main.route.RailType = AIRail.GetRailType(INSTANCE.main.route.SourceStation.s_Location); }
}
}
if (!success)
{ // it's bad we cannot build our source station, that's really bad !
if (cError.IsError()) { INSTANCE.main.route.Status = RouteStatus.DEAD; }
else { INSTANCE.buildDelay=2; return false; }
}
else { INSTANCE.main.route.Status=3; }
}
if (INSTANCE.main.route.Status==3)
{
if (INSTANCE.main.route.TargetStation == null)
{
if (INSTANCE.main.route.VehicleType == RouteType.RAIL)
{
if (INSTANCE.main.route.TargetProcess.IsTown && AITown.GetRating(INSTANCE.main.route.TargetProcess.ID, AICompany.COMPANY_SELF) < AITown.TOWN_RATING_POOR) cTileTools.SeduceTown(INSTANCE.main.route.TargetProcess.ID);
cTrack.SetRailType(INSTANCE.main.route.RailType);
}
success=INSTANCE.main.builder.BuildStation(false);
if (!success && cError.IsError()) INSTANCE.main.route.TargetProcess.ZeroProcess();
}
else {
success=true;
DInfo("Destination station is already build, we're reusing an existing one",0);
}
if (success)
{ // attach the new station object to the route, stationID of the new station is hold in TargetStation for road
INSTANCE.main.route.TargetStation=cStation.Load(INSTANCE.main.route.TargetStation);
if (!INSTANCE.main.route.TargetStation) { cError.RaiseError(); success= false; }
else INSTANCE.main.route.TargetStation.OwnerClaimStation(INSTANCE.main.route.UID);
}
if (!success)
{ // we cannot do destination station
if (cError.IsError()) INSTANCE.main.route.Status = RouteStatus.DEAD;
else { INSTANCE.buildDelay=2; return false; }
}
else { INSTANCE.main.route.Status=4 }
}
if (INSTANCE.main.route.Status==4) // pathfinding
{
success=INSTANCE.main.builder.BuildRoadByType();
if (success)
{ INSTANCE.main.route.Status=5; }
else {
if (cError.IsError()) { INSTANCE.main.route.Status = RouteStatus.DEAD; }
else { INSTANCE.buildDelay = 1; return false; }
} // and nothing more, stay at that phase & rebuild road when possible
}
if (INSTANCE.main.route.Status==5)
{ // check the route is really valid
if (INSTANCE.main.route.VehicleType == AIVehicle.VT_ROAD)
{
success=INSTANCE.main.builder.CheckRoadHealth(INSTANCE.main.route.UID);
}
else { success=true; } // other route type for now are ok
if (success) { INSTANCE.main.route.Status=6; }
else { INSTANCE.main.route.Status=RouteStatus.DEAD; }
}
if (INSTANCE.main.route.Status==6)
{
local bad = false;
if (!cMisc.ValidInstance(INSTANCE.main.route.SourceStation)) bad=true;
if (!bad && !cMisc.ValidInstance(INSTANCE.main.route.TargetStation)) bad=true;
if (!bad && !cMisc.ValidInstance(INSTANCE.main.route.SourceProcess)) bad=true;
if (!bad && !cMisc.ValidInstance(INSTANCE.main.route.TargetProcess)) bad=true;
if (!bad && !AIStation.IsValidStation(INSTANCE.main.route.SourceStation.s_ID)) bad=true;
if (!bad && !AIStation.IsValidStation(INSTANCE.main.route.TargetStation.s_ID)) bad=true;
if (bad) INSTANCE.main.route.Status=RouteStatus.DEAD;
else INSTANCE.main.route.Status=7;
}
if (INSTANCE.main.route.Status==7)
{
INSTANCE.main.route.RouteDone();
INSTANCE.main.route.RouteBuildGroup();
INSTANCE.main.route.Route_GroupNameSave();
DInfo("Route contruction complete ! "+INSTANCE.main.route.Name,0);
print("cargoID ="+cCargo.GetCargoLabel(INSTANCE.main.route.CargoID));
local srcprod=INSTANCE.main.route.SourceStation.IsCargoProduce(INSTANCE.main.route.CargoID);
local srcacc=INSTANCE.main.route.SourceStation.IsCargoAccept(INSTANCE.main.route.CargoID);
local dstprod=INSTANCE.main.route.TargetStation.IsCargoProduce(INSTANCE.main.route.CargoID);
local dstacc=INSTANCE.main.route.TargetStation.IsCargoAccept(INSTANCE.main.route.CargoID);
if (srcprod) INSTANCE.main.route.SourceStation.s_CargoProduce.AddItem(INSTANCE.main.route.CargoID,0);
if (srcacc) INSTANCE.main.route.SourceStation.s_CargoAccept.AddItem(INSTANCE.main.route.CargoID,0);
if (dstprod) INSTANCE.main.route.TargetStation.s_CargoProduce.AddItem(INSTANCE.main.route.CargoID,0);
if (dstacc) INSTANCE.main.route.TargetStation.s_CargoAccept.AddItem(INSTANCE.main.route.CargoID,0);
print("srcprod="+srcprod+" srcacc="+srcacc);
print("dstprod="+dstprod+" dstacc="+dstacc);
if (srcprod && srcacc && dstprod && dstacc)
{
DInfo("Route set as twoway",1);
INSTANCE.main.route.Twoway=true;
}
else {
DInfo("Route set as oneway",1);
INSTANCE.main.route.Twoway=false;
}
INSTANCE.main.builder.building_route=-1; // Allow us to work on a new route now
if (INSTANCE.safeStart >0 && INSTANCE.main.route.VehicleType == RouteType.ROAD) INSTANCE.safeStart--;
INSTANCE.main.route.DutyOnRoute();
}
if (INSTANCE.main.route.Status == RouteStatus.DEAD)
{
DInfo("TryBuildThatRoute mark "+INSTANCE.main.route.UID+" undoable",1);
INSTANCE.main.route.RouteIsNotDoable();
INSTANCE.main.builder.building_route=-1;
return false;
}
return success;
}
DictatorAI/build/stationremover.nut 0000644 0001750 0000144 00000005312 12203756370 017017 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cBuilder::DestroyStation(stationid)
// Remove a station from uid route
// Check no one else use it and the station is old enough before doing that
{
local exist=true;
if (stationid == null) return false;
local temp=cStation.Load(stationid);
if (!AIStation.IsValidStation(stationid))
{
cStation.DeleteStation(stationid);
return false;
}
local wasnamed = AIStation.GetName(stationid);
if (!temp) exist = false; // A case where a station exist but not in our station base
if (exist)
{ // check no route still use it
wasnamed = temp.s_Name;
if (temp.s_Owner.Count() != 0)
{
DInfo("Can't remove station "+wasnamed+" ! Station is still used by "+temp.s_Owner.Count()+" routes",1);
return false;
}
local now = AIDate.GetCurrentDate();
if (now - temp.s_DateBuilt < 30 && !AIController.GetSetting("infrastructure_maintenance"))
{
DInfo("Station "+wasnamed+" is not old enough. Keeping it for now.",1);
return false;
}
}
DInfo("Destroying station "+wasnamed,0);
if (exist)
{
if (temp.s_SubType != -2) //don't try destroy virtual station tiles, some player play with magic buldozer cheat
{
foreach (tile, dummy in temp.s_Tiles) { cTileTools.UnBlackListTile(tile); }
foreach (tile, dummy in temp.s_TilesOther) { cTileTools.UnBlackListTile(tile); }
if (!temp.s_TilesOther.IsEmpty())
{
if (temp.s_Type == AIStation.STATION_TRAIN) { cTrack.RailCleaner(temp.s_TilesOther); }
else { cTrack.RoadCleaner(temp.s_TilesOther); }
}
}
if (!cTrack.DestroyDepot(temp.s_Depot)) { DInfo("Fail to remove depot link to station "+wasnamed,1); }
else { DInfo("Removing depot link to station "+wasnamed,0); }
AIController.Sleep(1);
if (!cStation.DeleteStation(stationid)) { return false; }
if (temp.s_SubType == -2) { return true; }
}
local tilelist=cTileTools.FindStationTiles(AIStation.GetLocation(stationid));
tilelist.Valuate(AITile.GetOwner);
tilelist.KeepValue(AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)); // Prevent magic bulldozer destroying platform
foreach (tile, dummy in tilelist) AITile.DemolishTile(tile); // still rough to do that, could do it nicer
return true;
}
DictatorAI/class/ 0000755 0001750 0000144 00000000000 12241403155 013203 5 ustar krinn users DictatorAI/class/version.nut 0000755 0001750 0000144 00000001027 12072376347 015441 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
SELF_VERSION <- 170;
SELF_SHORTNAME <- "DCTR";
DictatorAI/class/cscp.nut 0000755 0001750 0000144 00000005411 12176351544 014702 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
import("Library.SCPLib", "SCPLib", 45);
require("version.nut");
class cSCP extends cClass
{
SCPInstance= null;
SCPTile = null;
goal_callback = null;
constructor()
{
this.ClassName="cSCP";
}
}
function cSCP::Init()
{
this.SCPInstance=SCPLib(SELF_SHORTNAME, SELF_VERSION, null);
this.SCPInstance.SCPLogging_Error(true);
this.SCPInstance.SCPLogging_Info(false);
this.SCPTile = SCPInstance.SCPGetCommunicationTile();
this.AddCommandSet();
}
function cSCP::WaitReady()
{
DInfo("Waiting SCP to get ready.",2);
cEngineLib.EngineCacheInit();
for (local j=0; j < 10; j++)
{
if (!this.SCPInstance.CanSpeakWith()) { AIController.Sleep(10); this.SCPInstance.Check(); }
else return;
}
this.GetCurrentGoal();
}
function cSCP::IsAllow()
{
return (AIController.GetSetting("allow_scp") == 1);
}
function cSCP::Check()
{
return SCPInstance.Check();
}
function cSCP::AddCommandSet()
{
SCPInstance.AddCommand("CurrentGoal", "NoCarGoal", INSTANCE, cSCP.GetCurrentGoalCallback);
SCPInstance.AddCommand("GSSetting", "NoCarGoal", INSTANCE, cSCP.ReceivedGSSettingCommand);
SCPInstance.AddCommand("GoalCompleted", "NoCarGoal", INSTANCE, cSCP.GoalComplete);
}
function cSCP::GoalComplete(message, self)
{
INSTANCE.main.SCP.DInfo("Goal complete for "+AICargo.GetCargoLabel(message.Data[0])+" "+message.Data[0],0);
INSTANCE.main.SCP.GetCurrentGoal();
}
function cSCP::GetCurrentGoal()
{
SCPInstance.QueryServer("CurrentGoal", "NoCarGoal", AICompany.ResolveCompanyID(AICompany.COMPANY_SELF));
}
function cSCP::GetCurrentGoalCallback(message, self)
{
AILog.Info("Receiving goals: ");
AILog.Info("Do "+message.Data[2]+" units of "+cCargo.GetCargoLabel(message.Data[1]));
AILog.Info("Do "+message.Data[5]+" units of "+cCargo.GetCargoLabel(message.Data[4]));
AILog.Info("Do "+message.Data[8]+" units of "+cCargo.GetCargoLabel(message.Data[7]));
local goal_to_do=AIList();
if (message.Data[3] < message.Data[2]) goal_to_do.AddItem(message.Data[1],0);
if (message.Data[6] < message.Data[5]) goal_to_do.AddItem(message.Data[4],0);
if (message.Data[9] < message.Data[8]) goal_to_do.AddItem(message.Data[7],0);
if (goal_to_do.IsEmpty()) return;
self.main.cargo.SetCargoFavorite(goal_to_do.Begin());
}
function cSCP::ReceivedGSSettingCommand(message, self)
{
AILog.Info("Received GSSetting Comnand answer");
}
DictatorAI/class/cclass.nut 0000755 0001750 0000144 00000007217 12203714436 015222 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cClass
{
ClassName = null;
constructor()
{
this.ClassName = "cClass";
}
}
function cClass::GetName()
{
return this.ClassName;
}
function cClass::DInfo(putMsg, debugValue=0)
// just output AILog message depending on debug level
{
INSTANCE.DInfo(putMsg, debugValue, this.GetName());
}
function cClass::DError(putMsg,debugValue=1)
// just output AILog message depending on debug level
{
INSTANCE.DError(putMsg, debugValue, this.GetName());
}
function cClass::DWarn(putMsg, debugValue=1)
// just output AILog message depending on debug level
{
INSTANCE.DWarn(putMsg, debugValue, this.GetName());
}
class cMain extends cClass
{
bank = null;
builder = null;
carrier = null;
route = null;
jobs = null;
bridge = null;
cargo = null;
SCP = null;
event = null;
constructor()
{
this.ClassName = "cMain";
SCP = cSCP();
bank = cBanker();
builder = cBuilder();
carrier = cCarrier();
jobs = cJobs();
route = cRoute();
bridge = cBridge();
cargo = cCargo();
event = cEvents();
}
}
function cMain::Init()
{
cEngineLib.EngineCacheInit();
SCP.Init();
AIRoad.SetCurrentRoadType(AIRoad.ROADTYPE_ROAD);
cTrack.SetRailType();
route.RouteInitNetwork();
cargo.SetCargoFavorite();
local pList=AITownList();
foreach (pID, dummy in pList) cProcess.AddNewProcess(pID, true);
local pList=AIIndustryList();
foreach (pID, dummy in pList) cProcess.AddNewProcess(pID, false);
SCP.WaitReady();
}
function cMain::CheckAccount()
{
local ourLoan = AICompany.GetLoanAmount();
local maxLoan = AICompany.GetMaxLoanAmount();
local cash = AICompany.GetBankBalance(AICompany.COMPANY_SELF);
local goodcash = bank.mincash;
if (INSTANCE.main.carrier.vehicle_cash < 0) { INSTANCE.main.carrier.vehicle_cash = 0; }
if (ourLoan == 0 && cash >= (bank.mincash * cBanker.GetInflationRate())) { bank.unleash_road=true; }
if (!cBanker.CanBuyThat(goodcash)) { DInfo("Low on cash, disabling build : "+goodcash,1); bank.canBuild=false; }
else { cBanker.RaiseFundsTo(goodcash); }
if (ourLoan +(4*AICompany.GetLoanInterval()) < maxLoan) { bank.canBuild=true; }
if (maxLoan > 2000000 && ourLoan > 0 && route.RouteIndexer.Count() > 6)
{ DInfo("Trying to repay loan",1); bank.canBuild=false; } // wait to repay loan
local veh=AIVehicleList();
if (INSTANCE.buildDelay > 0) { DInfo("Builds delayed: "+INSTANCE.buildDelay,1); bank.canBuild=false; }
if (INSTANCE.main.carrier.vehicle_cash >0 && !cBanker.CanBuyThat(INSTANCE.main.carrier.vehicle_cash)) { DInfo("Delaying build: we save money for upgrade",1); bank.canBuild=false; }
local veh=AIVehicleList();
if (veh.IsEmpty() && cRoute.database.len()==0)
{
DInfo("Forcing build: We have 0 vehicle running !");
bank.canBuild=true;
if (cJobs.rawJobs.IsEmpty()) { DInfo("Hard times going on, unleashing routes"); bank.unleash_road=true; }
} // we have 0 vehicles force a build
DWarn("canBuild="+bank.canBuild+" unleash="+bank.unleash_road+" building_main.route."+builder.building_route+" warTreasure="+carrier.warTreasure+" vehicle_cash="+carrier.vehicle_cash+" RemainJobs="+cJobs.jobDoable.Count()+" vehicle_wish="+carrier.vehicle_wishlist.Count(),1);
}
DictatorAI/class/cloader.nut 0000755 0001750 0000144 00000041727 12203135041 015354 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cLoader
{
constructor()
{
this.ClassName="cLoader";
}
}
function cLoader::RegisterStations()
// discover and register stations as openttd knows them
// there's still few infos our stations will lack out of this (sadly most of them are important ones, like depot)
{
DInfo("Looking the map for our stations",0);
local stations = AIStationList(AIStation.STATION_ANY);
foreach (stationID, _ in stations)
{
cStation.InitNewStation(stationID);
local pause=cLooper();
}
}
function cLoader::OldSaveWarn()
// Just output a warning for old savegame format
{
AILog.Error("WARNING");
AILog.Info("That savegame was made with DictatorAI version "+INSTANCE.main.carrier.vehicle_cash);
AILog.Info("I have add a compatibility loader to help restoring old savegames but it doesn't support all versions");
AILog.Info("If you re-save your game, it will be saved with the new save format.");
AILog.Error("WARNING");
AIController.Sleep(20);
}
function cLoader::VehicleFindStationInOrders(vehicle)
// browse vehicle orders and return index of order that target that stationID
{
local numorders=AIOrder.GetOrderCount(vehicle);
if (numorders==0) { return -1; }
local stainfo = -1;
for (local j=0; j < numorders; j++)
{
local tiletarget=AIOrder.GetOrderDestination(vehicle,AIOrder.ResolveOrderPosition(vehicle, j));
if (!AITile.IsStationTile(tiletarget)) continue;
stainfo = AIStation.GetStationID(tiletarget);
for (local k = 0; k < j; k++) AIOrder.RemoveOrder(vehicle, 0);
return stainfo;
}
return -1;
}
function cLoader::IndustryGuesser(staID, cargo, taker)
{
local statype = cStation.FindStationType(staID);
if (statype == -1) { return -1; }
local radius = AIStation.GetCoverageRadius(staID);
local tiles = cTileTools.GetTilesAroundPlace(AIStation.GetLocation(staID), AIStation.GetCoverageRadius(statype));
tiles.Valuate(AIIndustry.GetIndustryID);
indlist = AIList();
foreach (tile, id in tiles)
{
if (indlist.HasItem(id)) { continue; }
if (AIIndustry.IsValidIndustry(id)) { indlist.AddItem(id, AITile.GetCargoProduction(tile, cargo, 1, 1, radius)); }
}
if (taker) {
tiles.KeepAboveValue(1); // production
}
else {
tiles.Valuate(AIIndustry.IsCargoAccepted, cargo);
tiles.KeepValue(1);
}
if (tiles.IsEmpty()) { return -1; }
else { return tiles.Begin(); }
}
function cLoader::GroupGuesser()
{
local vehlist = AIVehicleList()
foreach (veh, _ in vehlist)
{
local cargoGuess = cCarrier.GetCurrentCargoType(veh);
if (cargoGuess == -1) continue;
local sourcestation = cLoader.VehicleFindStationInOrders(veh);
if (sourcestation == -1) continue;
local targetstation = cLoader.VehicleFindStationInOrders(veh);
if (targetstation == -1) continue;
local srctown = false;
local dsttown = false;
if (cargoGuess == cCargo.GetPassengerCargo()) { srctown = true; dsttown = true; }
local statype = cStation.FindStationType(statype);
if (statype == -1) continue;
local src_ind = -1; dst_ind = -1;
local tiles = cTileTools.GetTilesAroundPlace(AIStation.GetLocation(sourcestation), AIStation.GetCoverageRadius(statype));
if (srctown) { src_ind = AITile.GetNearestTown(AIStation.GetLocation(sourcestation)); }
else { src_ind = IndustryGuesser(sourcestation, cargoGuess, true); }
if (dsttown) { dst_ind = AITile.GetNearestTown(AIStation.GetLocation(targetstation)); }
else { dst_ind = IndustryGuesser(targetstation, cargoGuess, false); }
if (src_ind == -1 || dst_ind == -1) { continue; }
local grptype = AIVehicle.GetVehicleType(veh);
local grp = AIGroup.CreateGroup(veh);
local ok = cRoute.SetRouteGroupName(grp, src_ind, dst_ind, srctown, dsttown, cargoGuess, false, sourcestation, targetstation);
if (ok)
{
local vehgrp = AIVehicle.GetGroupID(veh);
local otherveh = AIList();
otherveh.AddItem(veh);
if (vehgrp != AIGroup.GROUP_DEFAULT)
{
otherveh = AIVehicleList_Group(vehgrp);
}
foreach (v, _ in otherveh)
{
AIGroup.MoveVehicle(grp);
vehlist.RemoveItem(v);
}
}
}
}
function cLoader::Load169()
// Load savegame from version 169 & 168
{
cLoader.OldSaveWarn();
DInfo("Loading savegame version "+INSTANCE.main.carrier.vehicle_cash);
local all_stations=INSTANCE.main.bank.unleash_road;
DInfo("...Restoring stations",0);
local iter=0;
local allcargos=AICargoList();
local all_routes=INSTANCE.main.carrier.vehicle_cash;
local saveit=true;
for (local i=0; i < all_stations.len(); i++)
{
saveit=true;
local stationID=all_stations[i];
local sobj = null;
if (AIStation.IsValidStation(stationID))
{
sobj = cStation.Load(stationID);
if (!sobj) sobj = cStation.InitNewStation(stationID);
if (!cMisc.ValidInstance(sobj)) saveit=false;
}
else saveit = false;
if (saveit) sobj.s_Depot=all_stations[i+1];
i+=1;
if (saveit && sobj instanceof cStationRail)
{
local counter=all_stations[i+1];
i+=2;
local temparray=[];
for (local z=0; z < counter; z++) temparray.push(all_stations[i+z]);
i+=(counter-1);
if (saveit) sobj.s_Platforms=cMisc.ArrayToList(temparray);
counter=all_stations[i+1];
i+=2;
temparray=[];
for (local z=0; z < counter; z++) temparray.push(all_stations[i+z]);
//if (saveit) sobj.s_TrainSpecs =cMisc.ArrayToList(temparray); // train plaforms, lost for now
i+=(counter-1);
}
}
iter++;
DInfo("Found "+iter+" stations.",0);
DInfo("...Restoring routes",0);
iter=0;
for (local i=0; i < all_routes.len(); i++)
{
saveit=true;
local obj=cRoute();
local temp;
local _rtype = all_routes[i];
local _groupid = all_routes[i+1];
local _one = all_routes[i+2];
local _two = all_routes[i+3];
local _three = all_routes[i+4];
local _four =all_routes[i+5];
i+=5;
iter++;
saveit = (_groupid != null);
local src_IsTown, dst_IsTown;
if (saveit)
{
local gname=AIGroup.GetName(_groupid);
// this version use A*CargoID*I###*T###*###*### groupname
local workarr = cMisc.SplitStars(gname);
if (workarr.len() != 0)
{
obj.CargoID = workarr[1].tointeger();
src_IsTown = (workarr[2].slice(0,1) == "T");
dst_IsTown = (workarr[3].slice(0,1) == "T");
}
temp=workarr[2].slice(1).tointeger(); // source id
obj.SourceProcess = cProcess.Load(cProcess.GetUID(temp, src_IsTown));
temp=workarr[3].slice(1).tointeger(); // target id
obj.TargetProcess = cProcess.Load(cProcess.GetUID(temp, dst_IsTown));
temp=workarr[4].tointeger(); // source station id
obj.SourceStation = cStation.Load(temp);
temp=workarr[5].tointeger(); // target station id
obj.TargetStation = cStation.Load(temp);
if (saveit) saveit = cMisc.ValidInstance(obj.SourceProcess);
if (saveit) saveit = cMisc.ValidInstance(obj.TargetProcess);
if (saveit) saveit = cMisc.ValidInstance(obj.SourceStation);
if (saveit) saveit = cMisc.ValidInstance(obj.TargetStation);
if (saveit) obj.VehicleType = _rtype;
obj.GroupID = _groupid;
}
if (saveit)
{
local jrt= obj.VehicleType;
local crg= obj.CargoID;
temp = cJobs();
temp.UID = null;
if (jrt >= RouteType.AIR) { crg=cCargo.GetPassengerCargo(); jrt=RouteType.AIR; }
temp.roadType = jrt;
temp.cargoID = crg;
temp.sourceObject = obj.SourceProcess;
temp.targetObject = obj.TargetProcess;
temp.GetUID();
obj.UID=temp.UID;
cJobs.CreateNewJob(obj.SourceProcess.UID, obj.TargetProcess.ID, crg, jrt, 0); // recreate the job
temp = cJobs.Load(obj.UID); // now load it
if (!temp) continue;
temp.isUse = true;
obj.SourceStation.OwnerClaimStation(obj.UID);
obj.TargetStation.OwnerClaimStation(obj.UID);
obj.VehicleType = jrt;
cRoute.SetRouteGroupName(obj.GroupID, obj.SourceProcess.ID, obj.TargetProcess.ID, obj.SourceProcess.IsTown, obj.TargetProcess.IsTown, obj.CargoID, false, obj.SourceStation.s_ID, obj.TargetStation.s_ID);
obj.Source_RailEntry = _one;
obj.Target_RailEntry = _two;
obj.Primary_RailLink = _three;
obj.Secondary_RailLink = _four;
obj.RouteDone();
DInfo("Validate... "+obj.Name,0);
}
}
DInfo("Found "+iter+" routes.",0);
DInfo("Restoring trains",0);
local all_trains=SixMonth;
iter=0;
for (local i=0; i < all_trains.len(); i++)
{
local obj=cTrain();
obj.vehicleID=all_trains[i];
obj.srcStationID=all_trains[i+1];
obj.dstStationID=all_trains[i+2];
obj.src_useEntry=all_trains[i+3];
obj.dst_useEntry=all_trains[i+4];
obj.stationbit=all_trains[i+5];
obj.full=false;
i+=5;
cTrain.vehicledatabase[obj.vehicleID] <- obj;
iter++;
}
DInfo("Found "+iter+" trains.",0);
cRoute.RouteRebuildIndex();
}
function cLoader::LoadSaveGame()
// Load current savegame version
{
DInfo("Loading savegame version "+INSTANCE.main.carrier.vehicle_cash);
local num_route_ok = 0;
local groupList = AIGroupList();
DInfo("Found "+groupList.Count()+" possible routes");
foreach(group, _ in groupList)
{
local temp_route = cRoute();
local dead_route = false;
temp_route.GroupID = group;
temp_route.VehicleType = AIGroup.GetVehicleType(group);
local gname = AIGroup.GetName(temp_route.GroupID);
local info = cMisc.SplitStars(gname);
if (info.len() != 7) { DInfo("Invalid route info length "+info.len(),1); continue; }
temp_route.CargoID = info[1].tointeger();
local src_IsTown = (info[2].slice(0,1) == "T");
local dst_IsTown = (info[3].slice(0,1) == "T");
local temp = info[2].slice(1).tointeger(); // source id
temp_route.SourceProcess = cProcess.Load(cProcess.GetUID(temp, src_IsTown));
temp = info[3].slice(1).tointeger(); // target id
temp_route.TargetProcess = cProcess.Load(cProcess.GetUID(temp, dst_IsTown));
temp = info[4].tointeger(); // source station id
if (temp_route.VehicleType == AIVehicle.VT_AIR && !src_IsTown) // chopper need the platform as station
{
local staID = AIStation.GetStationID(temp_route.SourceProcess.Location);
local t = cStation.InitNewStation(staID);
t.s_SubType = -2;
// TODO: fix platform
}
temp_route.SourceStation = cStation.Load(temp);
temp = info[5].tointeger(); // target station id
temp_route.TargetStation = cStation.Load(temp);
temp = info[6].tointeger(); // the info for rails
temp_route.Source_RailEntry = cMisc.CheckBit(temp, 0);
temp_route.Target_RailEntry = cMisc.CheckBit(temp, 1);
temp_route.Primary_RailLink = cMisc.CheckBit(temp, 2);
temp_route.Secondary_RailLink = cMisc.CheckBit(temp, 3);
if (!cMisc.ValidInstance(temp_route.SourceProcess)) { DInfo("Bad source process: "+temp_route.SourceProcess,1); dead_route = true; }
if (!cMisc.ValidInstance(temp_route.TargetProcess)) { DInfo("Bad target process: "+temp_route.SourceProcess,1); dead_route = true; }
if (!cMisc.ValidInstance(temp_route.SourceStation)) { DInfo("Bad source station: "+temp_route.SourceStation,1); dead_route = true; }
if (!cMisc.ValidInstance(temp_route.TargetStation)) { DInfo("Bad target station: "+temp_route.TargetStation,1); dead_route = true; }
if (dead_route) {
temp_route.Status = RouteStatus.DEAD;
local veh_list = AIVehicleList_Group(group);
if (!veh_list.IsEmpty())
{
foreach (veh, _ in veh_list) { AIVehicle.SendVehicleToDepot(veh); }
}
local wait = 0;
local count = veh_list.Count();
foreach (veh, _ in veh_list)
{
local removed = false;
while (wait < 100 && AIVehicle.GetState(veh) != AIVehicle.VS_IN_DEPOT)
{
AIController.Sleep(10);
wait++;
}
}
}
if (dead_route) { continue; }
if (temp_route.VehicleType == AIVehicle.VT_AIR)
{
if (AIStation.IsAirportClosed(temp_route.SourceStation.s_ID)) AIStation.OpenCloseAirport(temp_route.SourceStation.s_ID);
if (AIStation.IsAirportClosed(temp_route.TargetStation.s_ID)) AIStation.OpenCloseAirport(temp_route.TargetStation.s_ID);
}
temp = cJobs();
temp.UID = null;
temp.cargoID = temp_route.CargoID;
temp.roadType = temp_route.VehicleType;
if (temp.roadType >= RouteType.AIR) { temp.cargoID = cCargo.GetPassengerCargo(); temp.roadType = RouteType.AIR; }
temp.sourceObject = temp_route.SourceProcess;
temp.targetObject = temp_route.TargetProcess;
temp.GetUID();
temp_route.UID = temp.UID;
temp_route.Distance = AIMap.DistanceManhattan(temp_route.SourceProcess.Location, temp_route.TargetProcess.Location);
cJobs.CreateNewJob(temp_route.SourceProcess.UID, temp_route.TargetProcess.ID, temp.cargoID, temp.roadType, temp_route.Distance); // recreate the job
temp = cJobs.Load(temp_route.UID); // now try load it
if (!temp) continue;
temp.isUse = true;
temp_route.SourceStation.OwnerClaimStation(temp_route.UID);
temp_route.TargetStation.OwnerClaimStation(temp_route.UID);
temp_route.RouteDone();
DInfo("Validate... "+temp_route.Name,0);
num_route_ok++;
}
DInfo("Found "+num_route_ok+" routes");
DInfo("Restoring "+main.bank.unleash_road.len()+" stations");
{
for (local i=0; i < main.bank.unleash_road.len(); i++)
{
local sta = cStation.Load(main.bank.unleash_road[i]);
local valid = (sta != false);
if (!valid) { i++; AISign.BuildSign(main.bank.unleash_road[i], "D"); continue; }
local depot = main.bank.unleash_road[i+1]; i++;
sta.s_Depot = depot;
if (sta instanceof cStationRail)
{
sta.s_Train[0] = main.bank.unleash_road[i+1];
i++;
for (local j=0; j < sta.s_EntrySide.len(); j++)
{
sta.s_EntrySide[j] = main.bank.unleash_road[i+1];
i++;
}
for (local j=0; j < sta.s_ExitSide.len(); j++)
{
sta.s_ExitSide[j] = main.bank.unleash_road[i+1];
i++;
}
}
}
}
cRoute.RouteRebuildIndex();
RailFollower.FindRailOwner();
}
function cLoader::LoadOther()
{
cLoader.GroupGuesser();
cLoader.LoadSaveGame();
}
function cLoader::LoadingGame()
{
cLoader.RegisterStations();
local planelist=AIVehicleList_Group(INSTANCE.main.bank.mincash); // restore the network aircraft
foreach (veh, dummy in planelist) AIGroup.MoveVehicle(cRoute.VirtualAirGroup[0],veh);
planelist=AIVehicleList_Group(INSTANCE.TwelveMonth);
foreach (veh, dummy in planelist) AIGroup.MoveVehicle(cRoute.VirtualAirGroup[1],veh);
AIGroup.DeleteGroup(INSTANCE.main.bank.mincash);
AIGroup.DeleteGroup(TwelveMonth);
local trlist=AIVehicleList();
//try
//{
if (INSTANCE.main.carrier.vehicle_cash < 170) cLoader.Load169();
else cLoader.LoadSaveGame();
local grouplist = AIGroupList();
grouplist.RemoveList(cRoute.GroupIndexer);
foreach (grp, _ in grouplist) AIGroup.DeleteGroup(grp);
trlist.Valuate(AIVehicle.GetVehicleType);
trlist.KeepValue(AIVehicle.VT_RAIL);
trlist.Valuate(AIVehicle.GetState);
trlist.RemoveValue(AIVehicle.VS_RUNNING);
if (!trlist.IsEmpty())
{
DInfo("Restarting stopped trains",0);
foreach (veh, dummy in trlist)
{
cCarrier.TrainExitDepot(veh);
}
}
//} catch (z)
/* {
try { cLoader.LoadOther(); }
catch (e)
{
AILog.Error("Cannot load that savegame !");
AILog.Info("As a last chance, the AI will try to continue ignoring the error, with a total random result...");
local grouplist=AIGroupList();
grouplist.RemoveItem(cRoute.VirtualAirGroup[0]);
grouplist.RemoveItem(cRoute.VirtualAirGroup[1]);
foreach (grp, dummy in grouplist) AIGroup.DeleteGroup(grp);
local vehlist=AIVehicleList();
foreach (veh, dummy in vehlist) AIVehicle.SendVehicleToDepot(veh);
foreach (item in cRoute.database) if (item.UID > 1) delete cRoute.database[item.UID];
cRoute.RouteIndexer.Clear();
cRoute.GroupIndexer.Clear();
}
}*/
OneWeek=0;
OneMonth=0;
SixMonth=0;
TwelveMonth=0;
cRoute.RouteDamage.Clear(); // static, only clear it
INSTANCE.main.bank.canBuild=false;
INSTANCE.main.bank.unleash_road=false;
INSTANCE.main.carrier.vehicle_cash = 0;
INSTANCE.main.bank.mincash=10000;
cCargo.SetCargoFavorite();
local dead = AIList();
dead = AIVehicleList_DefaultGroup(AIVehicle.VT_ROAD);
trlist.AddList(AIVehicleList_DefaultGroup(AIVehicle.VT_AIR));
dead.AddList(trlist);
trlist.AddList(AIVehicleList_DefaultGroup(AIVehicle.VT_RAIL));
dead.AddList(trlist);
foreach (veh, _ in dead) AIVehicle.SendVehicleToDepot(veh); // reset ungroup vehicle so we will catch them fast
local alltowns=AITownList();
INSTANCE.main.builder.CheckRouteStationStatus();
}
DictatorAI/class/ccargo.nut 0000644 0001750 0000144 00000007002 12177725433 015206 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
// handle cargos
class cCargo extends cClass
{
static primaryCargo=[null,null,0,2]; // 0-passengerCargo, 1-mailCargo, 2-favoriteCargo 3-favoriteBonus
constructor()
{
this.ClassName="cCargo";
}
}
function cCargo::GetCargoFavorite()
// return the favorite cargo set
{
return cCargo.primaryCargo[2];
}
function cCargo::GetCargoFavoriteBonus()
// return the favorite cargo set
{
return cCargo.primaryCargo[3];
}
function cCargo::SetCargoFavoriteBonus(nbonus = 1)
// Set the bonus for production cargo, reset to 1 if no value is given
{
cCargo.primaryCargo[3]=nbonus;
}
function cCargo::SetCargoFavorite(cargoid = -1)
// Set our favorite cargo, this gave it a bonus
{
local cargo_favorite=cCargo.GetCargoFavorite();
if (cargo_favorite == cargoid) { return; }
if (cargoid == -1)
{
local crglist=AICargoList();
crglist.Valuate(AIBase.RandItem);
cargoid=crglist.Begin();
}
cargo_favorite=cargoid;
cCargo.primaryCargo[2]=cargoid;
DInfo("We will now promote "+cCargo.GetCargoLabel(cargo_favorite),0);
}
function cCargo::GetMailCargo()
// Return the cargo ID for mail
{
if (cCargo.primaryCargo[1] != null || cCargo.primaryCargo[1] == -1) { return cCargo.primaryCargo[1]; }
local cargolist = AICargoList();
foreach (cargo, dummy in cargolist) if (AICargo.GetTownEffect(cargo) == AICargo.TE_MAIL) { cCargo.primaryCargo[1]=cargo; break; }
if (cCargo.primaryCargo[1] == null) { DError("Cannot find mail cargo",1); cCargo.primaryCargo[1]=-1; return -1; }
else { DInfo("Mail cargo set to "+cCargo.primaryCargo[1]+"-"+AICargo.GetCargoLabel(cCargo.primaryCargo[1]),0); }
return cCargo.primaryCargo[1];
}
function cCargo::GetPassengerCargo()
// Return the cargo ID for passenger
{
if (cCargo.primaryCargo[0] != null || cCargo.primaryCargo[0] == -1) { return cCargo.primaryCargo[0]; }
local cargolist = AICargoList();
foreach (cargo, dummy in cargolist) if (AICargo.GetTownEffect(cargo) == AICargo.TE_PASSENGERS) { cCargo.primaryCargo[0]=cargo; break; }
if (cCargo.primaryCargo[0] == null) { DError("Cannot find passenger cargo",1); cCargo.primaryCargo[1]=-1; return -1; }
else { DInfo("Passenger cargo set to "+cCargo.primaryCargo[0]+"-"+AICargo.GetCargoLabel(cCargo.primaryCargo[0]),0); }
return cCargo.primaryCargo[0];
}
function cCargo::IsCargoForTown(cargo)
// return true if the cargo should be drop to a town
{
local effet= AICargo.GetTownEffect(cargo);
if (effet == AICargo.TE_NONE || effet == AICargo.TE_WATER) { return false; }
return true;
}
function cCargo::IsFreight(cargoID)
// Return -1 if !AICargo.IsFreight
// else return number of wagons need to add an extra engine depending on system setting
{
local level=AIGameSettings.GetValue("freight_trains");
if (level == 1) { return -1; } // no need to handle freight then
local freightlimit= 8 - level;
if (freightlimit < 3) { freightlimit=2; } // minimum 2 wagons
if (AICargo.IsFreight(cargoID)) { return freightlimit; }
return -1;
}
function cCargo::GetCargoLabel(cargo)
// return a formatted string for cargo
{
return AICargo.GetCargoLabel(cargo)+"("+cargo+")";
}
DictatorAI/class/cmisc.nut 0000755 0001750 0000144 00000011513 12203743202 015033 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cMisc
{
constructor()
{
}
}
function cMisc::PickCompanyName(who)
{
local nameo = ["Last", "For the", "Militia", "Revolution", "Good", "Bad", "Evil"];
local namet = ["Hope", "People", "Corporation", "Money", "Dope", "Cry", "Shot", "War", "Battle", "Fight"];
local x = 0; local y = 0;
if (who == 666) { x = AIBase.RandRange(7); y = AIBase.RandRange(10); }
else { x = who; y = who; }
return nameo[x]+" "+namet[y]+" (DictatorAI)";
}
function cMisc::SetPresident()
{
local myName = "Krinn's Company";
local FinalName = myName;
FinalName = cMisc.PickCompanyName(666);
AICompany.SetName(FinalName);
AICompany.SetPresidentGender(AICompany.GENDER_MALE);
DInfo("We're now "+FinalName,0);
local randomPresident = 666;
if (DictatorAI.GetSetting("PresidentName")) { randomPresident = AIBase.RandRange(12); }
local lastrand = "";
local nickrand = cMisc.getFirstName(randomPresident);
lastrand = nickrand + " "+ cMisc.getLastName(randomPresident);
DInfo(lastrand+" will rules the company with an iron fist",0);
AICompany.SetPresidentName(lastrand);
}
function cMisc::getLastName(who)
{
local names = ["Castro", "Mussolini", "Lenin", "Stalin", "Batista", "Jong", "Mugabe", "Al-Bashir", "Milosevic", "Bonaparte", "Caesar", "Tse-Tung"];
if (who == 666) { who = AIBase.RandRange(12) };
return names[who];
}
function cMisc::getFirstName(who)
{
local names = ["Fidel", "Benito", "Vladamir", "Joseph", "Fulgencio", "Kim", "Robert", "Omar", "Slobodan", "Napoleon", "Julius", "Mao"];
if (who == 666) { who = AIBase.RandRange(12) };
return names[who];
}
function cMisc::SetBit(value, bitset)
// Set the bit in value
{
value = value | (1 << bitset);
return value;
}
function cMisc::ClearBit(value, bitset)
// Clear a bit in value
{
value = value & ~(1 << bitset);
return value;
}
function cMisc::ToggleBit(value, bitset)
// Set/unset bit in value
{
value = value ^ (1 << bitset);
return value;
}
function cMisc::CheckBit(value, bitset)
// return true/false if bit is set in value
{
return ((value & (1 << bitset)) != 0);
}
function cMisc::checkHQ()
// Check and build our HQ if need
{
if (!AIMap.IsValidTile(AICompany.GetCompanyHQ(AICompany.COMPANY_SELF)))
{
local townlist = AITownList();
townlist.Valuate(AIBase.RandItem);
local tilelist = null;
tilelist = cTileTools.GetTilesAroundTown(townlist.Begin());
tilelist.Valuate(AIBase.RandItem);
foreach (tile, dummy in tilelist)
{
if (AICompany.BuildCompanyHQ(tile))
{
local name = AITown.GetName(AITile.GetClosestTown(tile));
DInfo("Built company headquarters near " + name,0);
return;
}
AIController.Sleep(1);
}
}
}
function GetCurrentGoalCallback(message, self)
{
DInfo("Received answer goal with ",2);
for (local i=0; i < message.Data.len(); i++) DInfo(" Goal #"+i+" - "+message.Data[i],2);
local goal_to_do=AIList();
if (message.Data[3] < message.Data[2]) goal_to_do.AddItem(message.Data[1],0);
if (message.Data[6] < message.Data[5]) goal_to_do.AddItem(message.Data[4],0);
if (message.Data[9] < message.Data[8]) goal_to_do.AddItem(message.Data[7],0);
if (goal_to_do.IsEmpty()) return;
INSTANCE.SetCargoFavorite(goal_to_do.Begin());
}
function cMisc::ListToArray(list)
{
local array = [];
local templist = AIList();
templist.AddList(list);
while (templist.Count() > 0) {
local arrayitem = [templist.Begin(), templist.GetValue(templist.Begin())];
array.append(arrayitem);
templist.RemoveTop(1);
}
return array;
}
function cMisc::ArrayToList(array)
{
local list = AIList();
local temparray = [];
temparray.extend(array);
while (temparray.len() > 0) {
local arrayitem = temparray.pop();
list.AddItem(arrayitem[0], arrayitem[1]);
}
return list;
}
function cMisc::ValidInstance(obj)
// return true if obj is an instance of something
{
return (typeof(obj) == "instance");
}
function cMisc::SplitStars(st)
// This split the string st into an array of string, the split is at each * in the st
// This is to cut off a group name infos and grab them easy
{
local retValue=[];
local buff="";
for (local i = 0; i < st.len(); i++)
{
local c = st.slice(i, i+1);
if (c == "*") { retValue.push(buff); buff=""; }
else buff+=c;
}
if (buff != "") retValue.push(buff); // add last one found because eol
return retValue;
}
function cMisc::IsAIList(f)
// return true if it's an AIList
{
if (typeof(f) != "instance") { return false; }
if (f instanceof AIList) { return true; }
return false;
}
DictatorAI/class/cstation.nut 0000644 0001750 0000144 00000047567 12241403155 015602 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
enum TrainSide
{
IN,
OUT,
CROSSING,
IN_LINK,
OUT_LINK,
DEPOT
}
enum TrainType
{
STATIONBIT,
TET,
TXT,
TED,
TXD,
START_POINT,
END_POINT,
DIRECTION,
DEPTH,
PLATFORM_LEFT,
PLATFORM_RIGHT,
OWNER,
GOODPLATFORM
}
class cStation extends cClass
{
static stationdatabase = {};
static VirtualAirports = AIList(); // stations in the air network as item, value=towns
static function GetStationObject(stationID)
{
return stationID in cStation.stationdatabase ? cStation.stationdatabase[stationID] : null;
}
s_ID = null; // id of station
s_Type = null; // AIStation.StationType
s_SubType = null; // Special subtype of station (depend on station), -2 if the station is virtual (own by industry platform...)
s_Location = null; // Location of station
s_Depot = null; // depot position and id are the same
s_Size = null; // size of station: road = number of stations, trains=width, airport=width*height
s_MaxSize = null; // maximum size a station could be
s_CargoProduce = null; // cargos ID produce at station, value = amount waiting
s_CargoAccept = null; // cargos ID accept at station, value = cargo rating
s_Radius = null; // radius of the station
s_VehicleCount = null; // vehicle using that station
s_VehicleMax = null; // max vehicle that station could handle. For rail : -1 open, -2 close, >0 pathfinder task # is running
s_VehicleCapacity = null; // total capacity of all vehicle using the station, item=cargoID, value=capacity
s_Owner = null; // list routes that own that station
s_DateLastUpdate = null; // record last date we update infos for the station
s_DateLastUpgrade = null; // record last date we try upgrade the station
s_MoneyUpgrade = null; // money we need for upgrading the station
s_Name = null; // station name
s_Tiles = null; // Tiles where the station is
s_TilesOther = null; // Tiles own by station that aren't station tiles
s_DateBuilt = null; // Date we add this station as an object
s_UpgradeTry = null; // Number of trys remaining to upgrade a station
constructor()
{
// * are saved variables
this.ClassName = "cStation";
this.s_ID = -1; // *
this.s_Type = -1;
this.s_SubType = -1;
this.s_Location = -1;
this.s_Depot = -1;
this.s_Size = 1;
this.s_MaxSize = 1;
this.s_CargoProduce = AIList();
this.s_CargoAccept = AIList();
this.s_Radius = 0;
this.s_VehicleCount = 0;
this.s_VehicleMax = 0;
this.s_VehicleCapacity = AIList();
this.s_Owner = AIList();
this.s_DateLastUpdate = null;
this.s_DateLastUpgrade = null;
this.s_MoneyUpgrade = 0;
this.s_Name = "Default Station Name";
this.s_Tiles = AIList();
this.s_TilesOther = AIList();
this.s_DateBuilt = AIDate.GetCurrentDate();
this.s_UpgradeTry = 3;
//this.s_Virtual = false;
}
}
// public
function cStation::GetStationName(_stationID)
// Return station name
{
local thatstation=cStation.Load(_stationID);
if (!thatstation) { return "invalid StationID(#"+_stationID+")"; }
return thatstation.s_Name;
}
function cStation::Load(_stationID)
// Get a station object
{
local thatstation=cStation.GetStationObject(_stationID);
if (thatstation == null) { DWarn("Invalid stationID : "+_stationID+" Cannot get object",1); return false; }
if (!AIStation.IsValidStation(thatstation.s_ID))
{
DWarn("Invalid station in base : "+thatstation.s_ID,1);
}
return thatstation;
}
function cStation::Save()
// Save the station in the database
{
if (this.s_ID == null) { DInfo("Not adding station #"+this.s_ID+" in database "+cStation.stationdatabase.len(),2); return; }
if (this.s_ID in cStation.stationdatabase)
{
DInfo("Station "+this.s_Name+" properties have been changed",2);
local sta=cStation.stationdatabase[this.s_ID];
local keepowner=AIList();
keepowner.AddList(sta.s_Owner);
delete cStation.stationdatabase[this.s_ID];
cStation.VirtualAirports.RemoveItem(this.s_ID);
this.s_Owner.AddList(keepowner);
this.s_DateLastUpgrade = AIDate.GetCurrentDate(); // block upgrade of the new station
}
this.SetStationName();
DInfo("Adding station : "+this.s_Name+" to station database",2);
cStation.stationdatabase[this.s_ID] <- this;
}
function cStation::DeleteStation(stationid)
// Delete the station from database if unused and old enough
{
local s = cStation.Load(stationid);
if (!s) { return false; }
if (s.s_Owner.Count() == 0) // no more own by anyone
{
DInfo("Removing station "+s.s_Name+" from station database",1);
foreach (tile, _ in s.s_Tiles) { cTileTools.UnBlackListTile(tile); }
delete cStation.stationdatabase[s.s_ID];
cStation.VirtualAirports.RemoveItem(s.s_ID);
return true;
}
else { DInfo("Keeping station "+s.s_Name+" as the station is still use by "+s.s_Owner.Count()+" routes",1); }
return false;
}
function cStation::FindStationType(stationid)
// return the first station type we found for that station
// -1 on error
{
if (!AIStation.IsValidStation(stationid)) { return -1; }
local stationtype=-1;
stationtype=AIStation.STATION_DOCK; // testing dock first, so platform are handle by cStationWater
if (AIStation.HasStationType(stationid, stationtype)) { return stationtype; }
stationtype=AIStation.STATION_AIRPORT;
if (AIStation.HasStationType(stationid, stationtype)) { return stationtype; }
stationtype=AIStation.STATION_TRAIN;
if (AIStation.HasStationType(stationid, stationtype)) { return stationtype; }
stationtype=AIStation.STATION_TRUCK_STOP;
if (AIStation.HasStationType(stationid, stationtype)) { return stationtype; }
stationtype=AIStation.STATION_BUS_STOP;
if (AIStation.HasStationType(stationid, stationtype)) { return stationtype; }
return -1;
}
function cStation::OwnerClaimStation(uid)
// Route UID claims ownership for that station
{
if (!this.s_Owner.HasItem(uid))
{
this.s_Owner.AddItem(uid, 1);
DInfo("Route "+cRoute.GetRouteName(uid)+" claims station "+this.s_Name+". "+this.s_Owner.Count()+" routes are sharing it",1);
this.UpdateStationInfos()
}
}
function cStation::OwnerReleaseStation(uid)
// Route unclaims the ownership for that station, ask to destroy the station if no more owner own it
{
if (this.s_Owner.HasItem(uid))
{
this.s_Owner.RemoveItem(uid);
DInfo("Route "+cRoute.GetRouteName(uid)+" release station "+this.s_Name+". "+this.s_Owner.Count()+" routes are sharing it",1);
this.UpdateStationInfos();
}
}
function cStation::InitNewStation(stationID)
// Create a station object depending on station type. Add the station to base and return the station object or null on error.
{
if (!AIStation.IsValidStation(stationID))
{
DError("Station #"+stationID+" doesn't exist");
return null;
}
local _StationType = cStation.FindStationType(stationID);
if (_StationType == -1) { DWarn("Couldn't determine station type use by station #"+stationID); }
local _Location = AIStation.GetLocation(stationID);
local _oldstation = cStation.GetStationObject(stationID); // lookout if we knows this one already
local _station = null;
_StationType = AIStation.STATION_BUS_STOP; // forcing the switch to goes where the foreach is to trigger the bug
//local nothing = 0; // make sure no foreach bug is bugging us, keep this here to prevent it
AIController.Break("BUG HERE");
switch (_StationType)
{
case AIStation.STATION_TRAIN:
_station = cStationRail();
_station.s_Tiles = cTileTools.FindStationTiles(_Location);
_station.s_Radius = AIStation.GetCoverageRadius(_StationType);
break;
case AIStation.STATION_DOCK:
_station = cStationWater();
_station.s_MaxSize = 1;
_station.s_Tiles = cTileTools.FindStationTiles(_Location);
_station.s_Size = 1;
_station.s_SubType = -1;
_station.s_Radius = AIStation.GetCoverageRadius(_StationType);
break;
case AIStation.STATION_BUS_STOP:
case AIStation.STATION_TRUCK_STOP:
_station = cStationRoad();
_station.s_MaxSize = INSTANCE.main.carrier.road_max;
_station.s_Tiles = cTileTools.FindStationTiles(_Location);
_station.s_Size = _station.s_Tiles.Count();
_station.s_SubType = AIRoad.ROADTYPE_ROAD;
if (AIRoad.HasRoadType(_Location, AIRoad.ROADTYPE_TRAM)) { _station.s_SubType = AIRoad.ROADTYPE_TRAM; }
foreach (item, value in _station.s_Tiles) {} // Without a foreach bug won't happen, so here's one to trigger it
_station.s_Tiles.Valuate(AIRoad.GetRoadStationFrontTile);
_station.s_Radius = AIStation.GetCoverageRadius(_StationType);
break;
case AIStation.STATION_AIRPORT:
_station = cStationAir();
_station.s_MaxSize = 1000; // airport size is limited by airport avaiability
_station.s_Tiles = cTileTools.FindStationTiles(_Location);
_station.s_Size = _station.s_Tiles.Count();
_station.s_SubType = AIAirport.GetAirportType(_Location);
_station.s_Radius = AIAirport.GetAirportCoverageRadius(_station.s_SubType);
_station.s_Depot = AIAirport.GetHangarOfAirport(_Location);
break;
}
// now common properties
print("typeof station ="+typeof(_station)); // And here you can see _station is not define bug
_station.s_Location = _Location;
_station.s_Type = _StationType;
_station.s_ID = stationID;
_station.s_DateBuilt = AIDate.GetCurrentDate();
_station.s_VehicleMax = 500;
_station.Save();
cStation.StationClaimTile(_station.s_Tiles, _station.s_ID);
if (_station instanceof cStationRail) { _station.GetRailStationMiscInfo(); }
return _station;
}
function cStation::CanUpgradeStation()
// check if station could be upgrade
// just return canUpgrade value or for airports true or false if we find a better airport
{
if (!cBanker.CanBuyThat(AICompany.GetLoanInterval())) { return false; }
local now = AIDate.GetCurrentDate();
if (this.s_DateLastUpgrade != null && now - this.s_DateLastUpgrade < 60) { return false; }
// if last time we try to upgrade we have fail and it was < 60 days, give up
if (!cBanker.CanBuyThat(this.s_MoneyUpgrade)) { return false; }
// we fail because we need that much money and we still don't have it
if (this.s_UpgradeTry < 1) { return false; }
switch (this.s_Type)
{
case AIStation.STATION_DOCK:
this.s_VehicleMax = INSTANCE.main.carrier.water_max;
return false;
break;
case AIStation.STATION_TRAIN:
if (this.s_Size >= this.s_MaxSize) { return false; }
return true;
break;
case AIStation.STATION_AIRPORT:
local canupgrade=false;
local newairport = cBuilder.GetAirportType();
// the per airport type limit doesn't apply to network aircrafts that bypass this check
local vehlist=AIVehicleList_Station(this.s_ID);
local townID=AIAirport.GetNearestTown(this.s_Location, this.s_SubType);
local townpop=AITown.GetPopulation(townID);
if (newairport > this.s_SubType && !vehlist.IsEmpty() && townpop >= (newairport*200))
{ canupgrade=true; DInfo("NEW AIRPORT AVAILABLE ! "+newairport,2); }
if (this.s_Tiles.Count()==1) { return false; } // plaforms have 1 size only
return canupgrade;
break;
default: // bus or truck
this.s_VehicleMax = this.s_Size * INSTANCE.main.carrier.road_upgrade;
if (this.s_Size >= this.s_MaxSize) { return false; }
else { return true; }
break;
}
return false;
}
function cStation::UpdateStationInfos()
// Update informations for that station if informations are old enough
{
local now = AIDate.GetCurrentDate();
if (this.s_DateLastUpdate != null && now - this.s_DateLastUpdate < 20) { DInfo("Station "+this.s_Name+" infos are fresh",2); return; }
DInfo("Refreshing station "+this.s_Name+" infos",2);
this.s_DateLastUpdate = now;
this.UpdateCapacity();
this.UpdateCargos();
}
function cStation::IsDepot(tile)
// return true if we have a depot at tile
{
if (tile == null) { return false; }
local isDepot=(AIMarine.IsWaterDepotTile(tile) || AIRoad.IsRoadDepotTile(tile) || AIRail.IsRailDepotTile(tile) || AIAirport.IsHangarTile(tile));
return isDepot;
}
// private functions
function cStation::SetStationName()
// set name of a station
{
if (!AIStation.IsValidStation(this.s_ID)) { this.s_Name = "Invalid Station (#"+this.s_ID+")"; return false; }
local n_type = "UNKNOWN";
switch (this.s_Type)
{
case AIStation.STATION_TRAIN:
n_type = "Train";
break;
case AIStation.STATION_TRUCK_STOP:
n_type = "Truck";
break;
case AIStation.STATION_BUS_STOP:
n_type = "Bus";
break;
case AIStation.STATION_AIRPORT:
n_type = "Airport";
break;
case AIStation.STATION_DOCK:
n_type = "Dock";
break;
}
this.s_Name = AIStation.GetName(this.s_ID)+"("+n_type+"#"+this.s_ID+")";
return true;
}
function cStation::StationClaimTile(tile, stationID, useEntry = -1)
/**
/* Add a tile or a list of tiles as own by StationID
/* @param tile : a tile or an AIList of tiles
/* @param stationID : the stationID to work with
/* @param useEntry : -1 to not care, true for entry, false for exit
**/
{
local station = cStation.Load(stationID);
if (!station) { return; }
local wlist = AIList();
if (cMisc.IsAIList(tile)) { wlist.AddList(tile); }
else { wlist.AddItem(tile, 0); }
if (wlist.IsEmpty()) { return; }
local value = -1;
if (useEntry != -1)
{
if (useEntry) { value = 1; }
else { value = 0; }
}
foreach (t, _ in wlist)
{
cTileTools.BlackListTile(t, stationID);
if (AITile.IsStationTile(t)) { station.s_Tiles.AddItem(t, value); }
else { station.s_TilesOther.AddItem(t, value); }
}
}
function cStation::StationReleaseTile(tile, stationID)
/**
/* Remove a tile as own by StationID
/* @param tile : a tile or an AIList of tiles
/* @param stationID : the stationID to work with
/* @param useEntry : -1 to not care, true for entry, false for exit
**/
{
local station = cStation.Load(stationID);
if (!station) { return; }
local wlist = AIList();
if (cMisc.IsAIList(tile)) { wlist.AddList(tile); }
else { wlist.AddItem(tile, 0); }
if (wlist.IsEmpty()) { return; }
foreach (t, _ in wlist)
{
cTileTools.UnBlackListTile(t);
if (AITile.IsStationTile(t)) { station.s_Tiles.RemoveItem(t); }
else { station.s_TilesOther.RemoveItem(t); }
}
}
function cStation::UpdateCargos(stationID=null)
// Update Cargos waiting & rating at station
// This function doesn't produce real cargo production/acceptance at station, but only the ones we care about
{
local thatstation=false;
if (stationID == null) { thatstation=this; }
else { thatstation=cStation.Load(stationID); }
if (!thatstation) { return; }
local allcargos=AIList();
allcargos.AddList(thatstation.s_CargoProduce);
allcargos.AddList(thatstation.s_CargoAccept);
foreach (cargo, value in allcargos)
{
if (thatstation.s_CargoProduce.HasItem(cargo))
{
local waiting=AIStation.GetCargoWaiting(thatstation.s_ID, cargo);
thatstation.s_CargoProduce.SetValue(cargo, waiting);
DInfo("Station "+thatstation.s_Name+" produce "+cCargo.GetCargoLabel(cargo)+" with "+waiting+" units",2);
}
if (thatstation.s_CargoAccept.HasItem(cargo))
{
local rating=AIStation.GetCargoRating(thatstation.s_ID, cargo);
thatstation.s_CargoAccept.SetValue(cargo, rating);
DInfo("Station "+thatstation.s_Name+" accept "+cCargo.GetCargoLabel(cargo)+" with "+rating+" rating",2);
}
local pause = cLooper();
}
}
function cStation::UpdateCapacity(stationID=null)
// Update the capacity of vehicles using the station
{
local thatstation=false;
if (stationID == null) { thatstation=this; }
else { thatstation=cStation.Load(stationID); }
if (!thatstation) { return; }
local vehlist=AIVehicleList_Station(thatstation.s_ID);
local allcargos=AICargoList();
local tmpcargos = AICargoList();
local mail = cCargo.GetMailCargo();
local pass = cCargo.GetPassengerCargo();
foreach (cargoID, dummy in tmpcargos)
{
local newcap = 0;
local short_list = [];
foreach (vehID, value in vehlist) if (value != -1) { short_list.push(vehID); }
foreach (vehID in short_list)
{
local capacity = AIVehicle.GetCapacity(vehID, cargoID);
if (capacity > 0)
{
// We will speedup checks, lowering vehicle list on each found cargo. It will then create a lost of cargo for multi-cargo vehicle
// like aircrafts that use mail/passenger, only mail or passenger will be count as the vehicle is removed from list.
// That's why we kept the vehicle for these cargos.
newcap += capacity;
if (cargoID != mail && cargoID != pass) { vehlist.SetValue(vehID, -1); }
}
local sleeper = cLooper();
}
allcargos.SetValue(cargoID, newcap);
if (newcap != thatstation.s_VehicleCapacity.GetValue(cargoID))
{
DInfo("Station "+thatstation.s_Name+" new total capacity set to "+newcap+" for "+cCargo.GetCargoLabel(cargoID),2);
}
}
thatstation.s_VehicleCapacity.Clear();
thatstation.s_VehicleCapacity.AddList(allcargos);
}
function cStation::IsCargoProduce(cargoID, stationID=null)
// return true/false if cargo is produce at that station
{
local thatstation=false;
if (stationID == null) { thatstation=this; }
else { thatstation=cStation.Load(stationID); }
if (!thatstation) { return false; }
foreach (tiles, sdummy in thatstation.s_Tiles)
{
local value=AITile.GetCargoProduction(tiles, cargoID, 1, 1, thatstation.s_Radius);
if (value > 0) { return true; }
}
return false;
}
function cStation::IsCargoAccept(cargoID, stationID=null)
// return true/false if cargo is accept at that station
{
local thatstation=false;
if (stationID == null) { thatstation=this; }
else { thatstation=cStation.Load(stationID); }
if (!thatstation) { return false; }
local cargoaccept = AICargoList_StationAccepting(thatstation.s_ID);
return cargoaccept.HasItem(cargoID);
}
function cStation::CheckCargoHandleByStation(stationID=null)
// Check what cargo is accept or produce at station
// This doesn't really check if all cargos are produce/accept, but only if the station know that cargo should be accept/produce
// This so, doesn't include any cargos no route handle, so station report only in use ones
// Use cStation.IsCargoAccept && cStation.IsCargoProduce for a real answers
// That function is there to faster checks (as it answer only cargo we care not all cargo the station can use), not to gave true answer
{
local thatstation = false;
if (stationID == null) { thatstation=this; }
else { thatstation=cStation.Load(stationID); }
if (!thatstation) { return; }
local test = AICargoList_StationAccepting(thatstation.s_ID);
if (thatstation.s_CargoAccept.Count() != test.Count())
{
thatstation.s_CargoAccept.Clear();
thatstation.s_CargoAccept.AddList(test);
DInfo("Station "+thatstation.s_Name+" cargo accepting list change : "+thatstation.s_CargoAccept.Count()+" cargos",1);
}
test = AIList();
foreach (cargo_id, cdummy in thatstation.s_CargoProduce)
{
foreach (tiles, sdummy in thatstation.s_Tiles)
{
local produce = AITile.GetCargoProduction(tiles, cargo_id, 1, 1, thatstation.s_Radius);
if (produce > 0) { test.AddItem(cargo_id, AIStation.GetCargoWaiting(thatstation.s_ID, cargo_id)); break; }
}
local pause = cLooper();
}
if (thatstation.s_CargoProduce.Count() != test.Count()) { DInfo("Station "+thatstation.s_Name+" cargo producing list change : "+test.Count()+" cargos",1); }
thatstation.s_CargoProduce.Clear();
thatstation.s_CargoProduce.AddList(test);
}
function cStation::GetLocation(stationID=null)
// avoid errors, return station location
{
local thatstation=null;
if (stationID == null) { thatstation=this; }
else { thatstation=cStation.Load(stationID); }
if (!thatstation) { return -1; }
if (thatstation.s_Type == AIStation.STATION_TRAIN) { return thatstation.s_Train[TrainType.START_POINT]; }
return AIStation.GetLocation(thatstation.s_ID);
}
DictatorAI/class/cjobs.nut 0000644 0001750 0000144 00000005627 12203171745 015052 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
// I've learned a lot from rondje's code about squirrel, thank you guys !
class cJobs extends cClass
{
static database = {};
static jobIndexer = AIList(); // this list have all UID in the database, value = date of last refresh for that UID infos
static jobDoable = AIList(); // same as upper, but only doable ones, value = ranking
static distanceLimits = [0, 0];// store [min, max] distances we can do, share to every instance so we can discover if this change
static TRANSPORT_DISTANCE=[60,150,250, 40,80,150, 40,100,130, 80,200,300]; // 130 max before water report dest_too_far
static CostTopJobs = [0,0,0,0];// price of best job for rail, road, water & air
static badJobs=AIList(); // List of jobs we weren't able to do
static WagonType=AIList(); // engine wagon to use for cargo : item=cargo, value= wagon engine id
static rawJobs=AIList(); // Primary jobs list, item (if industry=industryID, if town=townID+10000), value 0=done, >0=need handling
static TownAbuse = AIList(); // List of towns we use already to drop/take passenger/mail
static deadIndustry = AIList();// List all industries that are dead and so jobs using them need to be removed
static function GetJobObject(UID)
{
return UID in cJobs.database ? cJobs.database[UID] : null;
}
Name = null; // name of jobs
sourceObject = null; // source process object
targetObject = null; // target process object
cargoID = null; // cargo id
roadType = null; // RouteType type
UID = null; // a UID for the job
parentID = null; // a UID that a similar job will share with another (like other tansport or other destination)
isUse = null; // is build & in use
cargoValue = null; // value for that cargo
cargoAmount = null; // amount of cargo at source
distance = null; // distance from source to target
moneyToBuild = null; // money need to build the job
moneyGains = null; // money we should grab from doing the job
isdoable = null; // true if we can actually do that job (if isUse -> false)
ranking = null; // our ranking system
subsidy = null; // the subsity id aiming that job
constructor()
{
this.ClassName = "cJobs";
Name = "unknown job";
sourceObject = null;
targetObject = null;
cargoID = null;
roadType = null;
UID = null;
parentID = 0;
isUse = false;
subsidy = null;
cargoValue = 0;
cargoAmount = 0;
distance = 0;
moneyToBuild = 0;
moneyGains = 0;
isdoable = true;
ranking = 0;
}
}
DictatorAI/class/cerror.nut 0000777 0001750 0000144 00000006777 12204166776 015275 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cError extends cClass
{
static isCriticalError=[0]; // hold error flag
constructor()
{
this.ClassName="cError";
}
}
function cError::IsError()
// return true if error is raise
{
return cError.isCriticalError[0]==1;
}
function cError::ClearError()
// force unset the error flag
{
cError.SetErrorCritical(false);
}
function cError::RaiseError()
// force the error flag set
{
cError.SetErrorCritical(true);
}
function cError::SetErrorCritical(value)
// set/unset critical flag
{
if (value) cError.isCriticalError[0]=1;
else cError.isCriticalError[0]=0;
}
function cError::IsCriticalError()
// Check the last error to see if the error is a critical error or temp failure
// we return false when no error or true when error
// we set CriticalError to true for a critcal error or false for a temp failure
{
local lasterror=AIError.GetLastError();
local errcat=AIError.GetErrorCategory();
DInfo("Error check: "+AIError.GetLastErrorString()+" Cat: "+errcat+" flag: "+cError.IsError(),2);
if (cError.IsError()) return true; // return failure as long as flag is set
switch (lasterror)
{
case AIError.ERR_NOT_ENOUGH_CASH:
cError.SetErrorCritical(false);
cBanker.RaiseFundsBigTime();
return true;
break;
case AIError.ERR_NONE:
cError.SetErrorCritical(false);
return false;
break;
case AIError.ERR_VEHICLE_IN_THE_WAY:
cError.SetErrorCritical(false);
return true;
break;
case AIError.ERR_LOCAL_AUTHORITY_REFUSES:
cError.SetErrorCritical(false);
return true;
break;
case AIError.ERR_ALREADY_BUILT:
cError.SetErrorCritical(false);
return false; // let's fake we success in that case
break;
default:
cError.SetErrorCritical(true);
return true; // critical set
}
}
function cError::ForceAction(...)
/** @brief Loop until the action result is not block by a vehicle. Don't use it with functions that change arguments content
*
* @param ... The first param is the action, others are actions parameters (7 params only as YexoCallFunction limit)
*
*/
{
local action = null;
local action_param = [];
if (vargc < 1 || vargc > 8) return;
local output="";
local tile = -1;
for (local i = 0; i < vargc; i++)
{
if (i ==0) { action =vargv[i]; }
else {
action_param.push(vargv[i]);
if (i == 1) { tile = action_param[0]; } // assuming function use tile as first param
output += " "+vargv[i];
}
}
local result = -1;
local error = -666;
local count = 100;
local move = false;
while (error != AIError.ERR_NONE && count > 0)
{
count--;
result = cTileTools.YexoCallFunction(action, action_param);
error = AIError.GetLastError();
if (error != AIError.ERR_VEHICLE_IN_THE_WAY) { return result; }
DError("ForceAction delayed : tile="+tile+output,1);
if (!move)
{
move = true;
if (tile != -1 && tile != null && AIMap.IsValidTile(tile))
{
local veh = AIVehicleList();
veh.Valuate(AIVehicle.GetLocation);
veh.KeepValue(tile);
foreach (v, _ in veh) { AIVehicle.SendVehicleToDepotForServicing(v); }
}
}
AIController.Sleep(30);
}
return result;
}
DictatorAI/class/cstationroad.nut 0000644 0001750 0000144 00000001454 12051216214 016426 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cStationRoad extends cStation
{
constructor()
{
::cStation.constructor();
this.ClassName = "cStationRoad";
}
}
function cStationRoad::GetRoadStationEntry(entrynum=-1)
// return the front road station entrynum
{
if (entrynum == -1) entrynum=this.s_Tiles.Begin();
return this.s_Tiles.GetValue(entrynum);
}
DictatorAI/class/cbanker.nut 0000755 0001750 0000144 00000007530 12201674131 015350 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cBanker extends cClass
{
canBuild = null; // true if we can build new route
unleash_road = null; // true to build big road, false for small size
mincash = null;
basePrice = null; // it's just a base price cost to remove a rock tile
constructor()
{
unleash_road = false;
canBuild = true;
mincash = 10000;
basePrice = 0;
this.ClassName = "cBanker";
}
}
function cBanker::GetLoanValue(money)
// return amount to loan to have enough money
{
local i=0;
local loanStep=AICompany.GetLoanInterval();
while (money > 0) { i++; money-=loanStep; }
i--;
return (i*loanStep);
}
function cBanker::RaiseFundsTo(money)
{
local toloan = AICompany.GetLoanAmount() + money;
local curr=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
local success=true;
if (curr > money) success=true;
else success=AICompany.SetMinimumLoanAmount(toloan);
if (!success) { // can't get what we need, raising to what we could do so
DInfo("Cannot raise money to "+money+". Raising money to max we can",2);
toloan=AICompany.GetMaxLoanAmount();
success=AICompany.SetMinimumLoanAmount(toloan);
}
return success;
}
function cBanker::RaiseFundsBigTime()
// Raise our cash with big money, called when i'm going to spent a lot
{
//local max=(AICompany.GetMaxLoanAmount()*95/100)-AICompany.GetLoanAmount();
return cBanker.RaiseFundsBy(9 * AICompany.GetLoanInterval());
//if (AICompany.GetBankBalance(AICompany.COMPANY_SELF) < 2000000) INSTANCE.main.bank.RaiseFundsTo(AICompany.GetBankBalance(AICompany.COMPANY_SELF)+max);
// Don't use the loan if we have plenty cash
}
function cBanker::CanBuyThat(money)
// return true if we can spend money
{
local loan=AICompany.GetMaxLoanAmount()-AICompany.GetLoanAmount();
local cash=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
if (cash >= money) return true;
if (cash + loan < cash) return true;
if (cash + loan >= money) return true;
return false;
}
function cBanker::GetMaxMoneyAmount()
// return the max amount of cash we could raise
{
local loan=AICompany.GetMaxLoanAmount()-AICompany.GetLoanAmount();
local cash=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
if (cash+loan < cash) return cash; //overflow
return cash+loan;
}
function cBanker::SaveMoney()
// lower loan max to save money
{
local weare=AICompany.ResolveCompanyID(AICompany.COMPANY_SELF);
local balance=AICompany.GetBankBalance(weare);
DInfo("Saving our money",1);
local canrepay=cBanker.GetLoanValue(balance);
local newLoan=AICompany.GetLoanAmount()-canrepay;
if (newLoan <=0) newLoan=0;
AICompany.SetMinimumLoanAmount(newLoan);
}
function cBanker::RaiseFundsBy(money)
{
local curr = AICompany.GetBankBalance(AICompany.COMPANY_SELF);
if (curr < 0) curr=0;
if (money < curr) return true; // set we have the money to build it
local needed = money + curr;
return (cBanker.RaiseFundsTo(needed));
}
function cBanker::PayLoan()
{
local money = 0 - (AICompany.GetBankBalance(AICompany.COMPANY_SELF) - AICompany.GetLoanAmount()) + AICompany.GetLoanInterval();
if (money > 0)
{
if (AICompany.SetMinimumLoanAmount(money)) return true; else return false;
}
else {
if (AICompany.SetMinimumLoanAmount(0)) return true; else return false;
}
}
function cBanker::CashFlow()
{
this.PayLoan();
}
function cBanker::GetInflationRate()
{
return (AICompany.GetMaxLoanAmount() / (AIGameSettings.GetValue("difficulty.max_loan")) );
}
DictatorAI/class/cstationrail.nut 0000644 0001750 0000144 00000064152 12204103763 016440 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cStationRail extends cStation
{
s_EntrySide = null; // List holding entry related infos
// 0: entry_in tile where we should find the entry that lead to the station
// 1: entry_out
// 2: entry_crossing
// 3: entry_in_link tile to pathfind to enter the station by IN point
// 4: entry_out_link
// 5: entry_depot
s_ExitSide = null; // List holding exit related infos
// 0: exit_in tile where we should find the entry that lead to the station
// 1: exit_out
// 2: exit_crossing
// 3: exit_in_link tile to pathfind to enter the station by IN point
// 4: exit_out_link
// 5: exit_depot
s_Train = null; // Special list holding train infos
// 0: STATIONBIT, see bellow
// 1: number of train dropper using entry
// 2: number of train dropper using exit
// 3: number of train taker using entry
// 4: number of train taker using exit
// 5: startpoint : when created, tile where the start of station is == AIStation.GetLocation
// 6: endpoint : when created, tile where the end of station is
// 7: direction: when created, the direction of the station return by AIRail.GetRailStationDirection
// 8: depth: when created, the lenght (depth) the station is, for its width, "cstation.size" keep that info
// 9: most left platform position
// 10: most right platform position
// 11: main route owner
// 12: it's the number of all working platforms
/* STATIONBIT
bit0 entry is built 1 / not working 0
bit1 exit is built 1 / not working 0
bit2 as source station entry is use 1, exit is use 0
bit3 as target station entry is use 1, exit is use 0
bit4 main train line fire done yes 1/0 no
bit5 alt train line fire done yes 1/0 no
bit6 main line exist
bit7 alt line exist
*/
s_Platforms = null; // AIList of platforms: item=platform location
// value= bit0 on/off entry status
// value= bit1 on/off exit status
// value= bit2 on/off healthy connect
constructor()
{
::cStation.constructor();
this.ClassName="cStationRail";
this.s_Train = array(15,-1);
this.s_Platforms = AIList();
this.s_EntrySide = array(6,-1);
this.s_ExitSide = array(6,-1);
this.s_Train[TrainType.STATIONBIT]=3; // enable IN & OUT of station
this.s_Train[TrainType.TET]=0;
this.s_Train[TrainType.TED]=0;
this.s_Train[TrainType.TXT]=0;
this.s_Train[TrainType.TXD]=0;
this.s_Train[TrainType.GOODPLATFORM]=1;
}
}
function cStationRail::GetRailStationMiscInfo(stationID=null)
// Setup misc infos about a station, we shouldn't use that function direcly as it's an helper to cStation::InitNewStation()
// stationID: the stationID to check
{
local thatstation = false;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local stalength=0;
local entrypos=AIStation.GetLocation(thatstation.s_ID);
local stationtype = AIRail.GetRailType(entrypos);
thatstation.s_SubType = stationtype;
/*local statiles = cTileTools.FindStationTiles(entrypos);
thatstation.s_Tiles.AddList(statiles);*/
local statiles = AIList();
statiles.AddList(thatstation.s_Tiles);
statiles.Valuate(AIRail.GetRailType);
statiles.RemoveValue(stationtype);
if (!statiles.IsEmpty())
{
cBanker.RaiseFundsBigTime();
DInfo("Mismatch in station railtype... "+statiles.Count()+" are wrong");
foreach (tiles, _ in statiles)
{
cTrack.ConvertRailType(tiles, stationtype);
}
}
local direction, frontTile, backTile=null;
direction=AIRail.GetRailStationDirection(entrypos);
if (direction == AIRail.RAILTRACK_NW_SE)
{
frontTile=AIMap.GetTileIndex(0,-1);
backTile=AIMap.GetTileIndex(0,1);
}
else { // NE_SW
frontTile=AIMap.GetTileIndex(-1,0);
backTile=AIMap.GetTileIndex(1,0);
}
cDebug.ClearSigns();
local exitpos=null;
DInfo("Detecting station depth",1);
cDebug.PutSign(entrypos,"Start");
local scanner=entrypos;
while (AIRail.IsRailStationTile(scanner) && AIStation.GetStationID(scanner) == thatstation.s_ID)
{
stalength++;
scanner+=backTile;
cDebug.PutSign(scanner,".");
}
exitpos=scanner+frontTile;
cDebug.PutSign(exitpos,"End");
DInfo("Station "+thatstation.s_Name+" depth is "+stalength+" direction="+direction+" start="+entrypos+" end="+exitpos+ " type="+AIRail.GetName(stationtype),1);
thatstation.s_Train[TrainType.START_POINT]= entrypos;
thatstation.s_Train[TrainType.END_POINT]= exitpos;
thatstation.s_Train[TrainType.DIRECTION]= direction;
thatstation.s_Train[TrainType.DEPTH]= stalength;
thatstation.s_MaxSize = INSTANCE.main.carrier.rail_max;
thatstation.DefinePlatform();
cDebug.ClearSigns();
}
function cStationRail::GetRailStationFrontTile(entry, platform, stationID=null)
// like AIRail.GetRailDepotFrontTile but with a rail station
// entry: true to return front tile of the station entry, else front tile of station exit (end of station)
// platform: the platform location to find entry/exit front tile
// stationID: the rail stationID
// return -1 on error
{
local thatstation = false;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local direction=thatstation.s_Train[TrainType.DIRECTION];
local start=thatstation.s_Train[TrainType.START_POINT];
local end=thatstation.s_Train[TrainType.END_POINT];
local frontTile=null;
if (direction==AIRail.RAILTRACK_NE_SW)
{
start=AIMap.GetTileIndex(AIMap.GetTileX(start),AIMap.GetTileY(platform))+AIMap.GetTileIndex(-1,0);
end=AIMap.GetTileIndex(AIMap.GetTileX(end),AIMap.GetTileY(platform))+AIMap.GetTileIndex(1,0);
}
else
{
start=AIMap.GetTileIndex(AIMap.GetTileX(platform),AIMap.GetTileY(start))+AIMap.GetTileIndex(0,-1);
end=AIMap.GetTileIndex(AIMap.GetTileX(platform),AIMap.GetTileY(end))+AIMap.GetTileIndex(0,1);
}
if (entry) frontTile=start;
else frontTile=end;
cDebug.PutSign(frontTile,"Front="+entry);
cDebug.ClearSigns();
return frontTile;
}
function cStationRail::IsRailStationEntryOpen(stationID=null)
// return true if station entry bit is set
{
local thatstation=false;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local entry=thatstation.s_Train[TrainType.STATIONBIT];
if (cMisc.CheckBit(entry,0)) { DInfo("Station "+thatstation.s_Name+" entry is open",2); return true; }
DInfo("Station "+thatstation.s_Name+" entry is CLOSE",2);
return false;
}
function cStationRail::IsRailStationExitOpen(stationID=null)
// return true if station exit bit is set
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local exit=thatstation.s_Train[TrainType.STATIONBIT];
if (cMisc.CheckBit(exit,1)) { DInfo("Station "+thatstation.s_Name+" exit is open",2); return true; }
DInfo("Station "+thatstation.s_Name+" exit is CLOSE",2);
return false;
}
function cStationRail::RailStationCloseEntry(stationID=null)
// Unset entry bit of the station
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local entry=thatstation.s_Train[TrainType.STATIONBIT];
entry=cMisc.ClearBit(entry, 0); //entry=entry ^ 1;
thatstation.s_Train[TrainType.STATIONBIT]= entry;
DInfo("Closing the entry of station "+thatstation.s_Name,1);
}
function cStationRail::RailStationCloseExit(stationID=null)
// Unset exit bit of the station
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local exit=thatstation.s_Train[TrainType.STATIONBIT];
exit=cMisc.ClearBit(exit, 1); //exit=exit ^ 2;
thatstation.s_Train[TrainType.STATIONBIT]= exit;
DInfo("Closing the exit of station "+thatstation.s_Name,1);
}
function cStationRail::RailStationSetPrimarySignalBuilt(stationID=null)
// set the flag for the main rail signals status
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local entry=thatstation.s_Train[TrainType.STATIONBIT];
entry=cMisc.SetBit(entry, 4);
thatstation.s_Train[TrainType.STATIONBIT]= entry;
}
function cStationRail::RailStationSetSecondarySignalBuilt(stationID=null)
// set the flag for the secondary rail signals status
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local entry=thatstation.s_Train[TrainType.STATIONBIT];
entry=cMisc.SetBit(entry, 5);
thatstation.s_Train[TrainType.STATIONBIT]= entry;
}
function cStationRail::IsRailStationPrimarySignalBuilt(stationID=null)
// return true if the primary rail signals are all built on it
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local exit=thatstation.s_Train[TrainType.STATIONBIT];
if (cMisc.CheckBit(exit, 4))
{
DInfo("Station "+thatstation.s_Name+" signals are built on primary track",2);
return true;
}
return false;
}
function cStationRail::IsRailStationSecondarySignalBuilt(stationID=null)
// return true if the secondary rail signals are all built on it
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local exit=thatstation.s_Train[TrainType.STATIONBIT];
if (cMisc.CheckBit(exit, 5))
{
DInfo("Station "+thatstation.s_Name+" signals are built on secondary track",2);
return true;
}
return false;
}
function cStationRail::IsPrimaryLineBuilt(stationID=null)
// return true if the primary rail line is built
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local exit=thatstation.s_Train[TrainType.STATIONBIT];
return cMisc.CheckBit(exit, 6);
}
function cStationRail::IsAlternateLineBuilt(stationID=null)
// return true if the alternate rail line is built
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local exit=thatstation.s_Train[TrainType.STATIONBIT];
return cMisc.CheckBit(exit, 7);
}
function cStationRail::SetPrimaryLineBuilt(stationID=null)
// Set the primary rail line state as built
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local flag = thatstation.s_Train[TrainType.STATIONBIT];
thatstation.s_Train[TrainType.STATIONBIT] = cMisc.SetBit(flag, 6);
}
function cStationRail::SetAlternateLineBuilt(stationID=null)
// Set the alternate rail line state as built
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local flag = thatstation.s_Train[TrainType.STATIONBIT];
thatstation.s_Train[TrainType.STATIONBIT] = cMisc.SetBit(flag, 7);
}
function cStationRail::SetPlatformWorking(platformID, status, stationID = null)
// Set or unset the working state of a platform
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return false;
if (!thatstation.s_Platforms.HasItem(platformID)) return false;
local platf = thatstation.s_Platforms.GetValue(platformID);
if (status) platf = cMisc.SetBit(platf, 2);
else platf = cMisc.ClearBit(platf, 2);
thatstation.s_Platforms.SetValue(platformID, platf);
}
function cStationRail::IsPlatformWorking(platformID, stationID = null)
// return true if the platform is working (connect to the rails route)
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return false;
if (!thatstation.s_Platforms.HasItem(platformID)) return false;
local platvalue = thatstation.s_Platforms.GetValue(platformID);
if (cMisc.CheckBit(platvalue, 2)) return true;
return false;
}
function cStationRail::GetRailStationIN(getEntry, stationID=null)
// Return the tile where the station IN point is
// getEntry = true to return the entry IN, false to return exit IN
{
local thatstation=null;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local entryIN=thatstation.s_EntrySide[TrainSide.IN];
local exitIN=thatstation.s_ExitSide[TrainSide.IN];
if (getEntry) return entryIN;
else return exitIN;
}
function cStationRail::GetRailStationOUT(getEntry, stationID=null)
// Return the tile where the station OUT point is
// getEntry = true to return the entry OUT, false to return exit OUT
{
local thatstation=false;
if (stationID==null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local entryOUT=thatstation.s_EntrySide[TrainSide.OUT];
local exitOUT=thatstation.s_ExitSide[TrainSide.OUT];
if (getEntry) return entryOUT;
else return exitOUT;
}
function cStationRail::GetRailStationDirection() { return this.s_Train[TrainType.DIRECTION]; }
// avoid errors by returning proper index for direction of a station
function cStationRail::IsPlatformOpen(platformID, useEntry)
// check if a platform entry or exit is usable
{
local platindex=cStationRail.GetPlatformIndex(platformID);
if (platindex == -1) { DError("Bad platform index",1); return false; }
local stationID=AIStation.GetStationID(platformID);
if (stationID == null) return -1;
local thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local statusbit=thatstation.s_Platforms.GetValue(platindex);
if (useEntry) return (cMisc.CheckBit(statusbit, 0));
else return (cMisc.CheckBit(statusbit, 1));
}
function cStationRail::DefinePlatform(stationID=null)
// look out a train station and add every platforms we found
{
local thatstation=false;
if (stationID == null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local priorUpdate = thatstation.s_Platforms.Count();
local frontTile, backTile, leftTile, rightTile= null;
local direction=thatstation.GetRailStationDirection();
local staloc=thatstation.GetLocation();
if (direction == AIRail.RAILTRACK_NW_SE)
{
frontTile=AIMap.GetTileIndex(0,-1);
backTile=AIMap.GetTileIndex(0,1);
leftTile=AIMap.GetTileIndex(1,0);
rightTile=AIMap.GetTileIndex(-1,0);
}
else { // NE_SW
frontTile=AIMap.GetTileIndex(-1,0);
backTile=AIMap.GetTileIndex(1,0);
leftTile=AIMap.GetTileIndex(0,-1);
rightTile=AIMap.GetTileIndex(0,1);
}
local lookup=0;
local start=staloc;
local end=thatstation.s_Train[TrainType.END_POINT];
local topLeftPlatform=start;
local topRightPlatform=start;
// search up
while (AIRail.IsRailStationTile(lookup+start) && (AIStation.GetStationID(lookup+start)==thatstation.s_ID))
{
cDebug.PutSign(lookup+start,"*");
topLeftPlatform=lookup+start;
if (!thatstation.s_Platforms.HasItem(lookup+start)) thatstation.s_Platforms.AddItem(lookup+start,0);
lookup+=leftTile;
}
// search down
lookup=rightTile;
while (AIRail.IsRailStationTile(lookup+start) && (AIStation.GetStationID(lookup+start)==thatstation.s_ID))
{
cDebug.PutSign(lookup+start,"*");
topRightPlatform=lookup+start;
if (!thatstation.s_Platforms.HasItem(lookup+start)) thatstation.s_Platforms.AddItem(lookup+start,0);
lookup+=rightTile;
}
local goodCounter=0;
local runTarget=cStationRail.RailStationGetRunnerTarget(thatstation.s_ID);
cDebug.PutSign(runTarget,"RT");
local plat_temp=AIList();
plat_temp.AddList(thatstation.s_Platforms);
foreach (platidx, value in plat_temp)
{
if (thatstation.IsPlatformWorking(platidx)) { goodCounter++; continue; }
if (runTarget == -1) break;
if (!cMisc.CheckBit(value,0) && cBuilder.RoadRunner(platidx, runTarget, AIVehicle.VT_RAIL))
{
value = cMisc.SetBit(value, 0); // for now, assume both entry/exit are working
value = cMisc.SetBit(value, 1);
}
//if (!cMisc.CheckBit(value,1) && cBuilder.RoadRunner(platidx, runTarget, AIVehicle.VT_RAIL)) value=cMisc.SetBit(value,1);
thatstation.s_Platforms.SetValue(platidx, value);
if (cMisc.CheckBit(value,0) || cMisc.CheckBit(value, 1)) { goodCounter++; thatstation.SetPlatformWorking(platidx, true); }
}
DInfo("Station "+thatstation.s_Name+" have "+thatstation.s_Platforms.Count()+" platforms, "+goodCounter+" platforms are ok",2);
thatstation.s_Train[TrainType.GOODPLATFORM]=goodCounter;
thatstation.s_Size=thatstation.s_Platforms.Count();
thatstation.s_Train[TrainType.PLATFORM_LEFT]=topLeftPlatform;
thatstation.s_Train[TrainType.PLATFORM_RIGHT]=topRightPlatform;
if (thatstation.s_Platforms.Count() > 1 && !thatstation.IsPlatformWorking(topLeftPlatform) && !thatstation.IsPlatformWorking(topRightPlatform) && thatstation.s_Platforms.Count() == priorUpdate)
{
DInfo("Closing station "+thatstation.s_Name+" as it cannot grow anymore",1);
thatstation.s_MaxSize = thatstation.s_Size;
}
cDebug.ClearSigns();
}
function cStationRail::RailStationGetRunnerTarget(runnerID)
// return the tile location where we could use RoadRunner for checks
// this is the rail entry or exit from the main or secondary track, depending what the station can handle
// -1 on error
{
local thatstation=cStation.Load(runnerID);
if (!thatstation || thatstation.s_Owner.Count()==0) return -1;
local mainOwner=cRoute.Load(thatstation.s_Owner.Begin());
if (!mainOwner) return -1;
local primary=(mainOwner.SourceStation.s_ID == runnerID);
if (primary)
{
if (mainOwner.Source_RailEntry) return thatstation.s_EntrySide[TrainSide.IN];
else return thatstation.s_ExitSide[TrainSide.IN];
}
else {
if (mainOwner.Target_RailEntry) return thatstation.s_EntrySide[TrainSide.OUT];
else return thatstation.s_ExitSide[TrainSide.OUT];
}
return -1;
}
function cStationRail::GetPlatformFrontTile(platform, useEntry)
// return the front tile of the platform
// useEntry : true to return front tile of the platform entry, false to return one for exit
{
local platindex=cStationRail.GetPlatformIndex(platform, useEntry);
if (platindex==-1) return -1;
local stationID=AIStation.GetStationID(platform);
local front=cStationRail.GetRelativeTileForward(stationID, useEntry);
return platindex+front;
}
function cStationRail::GetPlatformIndex(platform, useEntry=true)
// return the platform reference (it's the station platform start location)
// useEntry: true return the real reference in our .s_Platforms, false = return the location of the exit for that platform
// the useEntry=false query shouldn't be use as index as only the start is record, you will fail to find the platform with that index
// -> it mean don't use it when trying to find a platform in cStation.s_Platforms list
// on error return -1
{
local stationID=AIStation.GetStationID(platform);
local thatstation=cStation.Load(stationID);
if (!thatstation) { DError("Invalid platform : "+platform,2); return -1; }
if (thatstation.s_Type != AIStation.STATION_TRAIN) { DError("Not a rail station",1); return -1; }
local platX=AIMap.GetTileX(platform);
local platY=AIMap.GetTileY(platform);
local staX=0;
local staY=0;
if (useEntry)
{
staX=AIMap.GetTileX(thatstation.s_Train[TrainType.START_POINT]); // X=SW->NE
staY=AIMap.GetTileY(thatstation.s_Train[TrainType.START_POINT]); // Y=SE->NW
}
else
{
staX=AIMap.GetTileX(thatstation.s_Train[TrainType.END_POINT]);
staY=AIMap.GetTileY(thatstation.s_Train[TrainType.END_POINT]);
}
if (thatstation.GetRailStationDirection()==AIRail.RAILTRACK_NE_SW)
{ staY=platY; }
else
{ staX=platX; }// NW_SE
return AIMap.GetTileIndex(staX, staY);
}
function cStationRail::GetRelativeDirection(stationID, dirswitch)
// return a tile index relative to station direction and its entry/exit
// stationID: the station to get relative direction
// dirswitch: 0- left, 1-right, 2-forward, 3=backward : add 10 to get exit relative direction
// return -1 on error
{
local loc=AIStation.GetLocation(stationID);
if (!AIRail.IsRailStationTile(loc)) { DError("Not a rail station tile",2); return -1; }
local dir=AIRail.GetRailStationDirection(loc);
local left, right, forward, backward = null;
if (dir==AIRail.RAILTRACK_NW_SE)
{
left=AIMap.GetTileIndex(1,0);
right=AIMap.GetTileIndex(-1,0);
forward=AIMap.GetTileIndex(0,-1);
backward=AIMap.GetTileIndex(0,1);
if (dirswitch >= 10) // SE->NW
{
left=AIMap.GetTileIndex(-1,0);
right=AIMap.GetTileIndex(1,0);
forward=AIMap.GetTileIndex(0,1);
backward=AIMap.GetTileIndex(0,-1);
}
}
else { // NE->SW
left=AIMap.GetTileIndex(0,-1);
right=AIMap.GetTileIndex(0,1);
forward=AIMap.GetTileIndex(-1,0);
backward=AIMap.GetTileIndex(1,0);
if (dirswitch >= 10) // SW->NE
{
left=AIMap.GetTileIndex(0,1);
right=AIMap.GetTileIndex(0,-1);
forward=AIMap.GetTileIndex(1,0);
backward=AIMap.GetTileIndex(-1,0);
}
}
if (dirswitch >=10) dirswitch-=10;
switch (dirswitch)
{
case 0:
return left;
case 1:
return right;
case 2:
return forward;
case 3:
return backward;
}
return -1;
}
function cStationRail::GetRelativeTileLeft(stationID, useEntry)
{
local value=0;
if (!useEntry) value+=10;
return cStationRail.GetRelativeDirection(stationID, value);
}
function cStationRail::GetRelativeTileRight(stationID, useEntry)
{
local value=1;
if (!useEntry) value+=10;
return cStationRail.GetRelativeDirection(stationID, value);
}
function cStationRail::GetRelativeTileForward(stationID, useEntry)
{
local value=2;
if (!useEntry) value+=10;
return cStationRail.GetRelativeDirection(stationID, value);
}
function cStationRail::GetRelativeTileBackward(stationID, useEntry)
{
local value=3;
if (!useEntry) value+=10;
return cStationRail.GetRelativeDirection(stationID, value);
}
function cStationRail::GetRelativeCrossingPoint(platform, useEntry)
// return crossing point relative to the platform, that's the point where front of station X axe meet crossing Y axe
// platform: the platform to find the relative crossing point
// useEntry: true to get crossing entry point, false for exit crossing point
{
local frontTile=cStationRail.GetPlatformFrontTile(platform, useEntry);
if (frontTile==-1) return -1;
local stationID=AIStation.GetStationID(platform);
local thatstation=cStation.Load(stationID);
if (!thatstation) return -1;
local crossing=0;
local direction=thatstation.GetRailStationDirection();
if (useEntry) crossing=thatstation.s_EntrySide[TrainSide.CROSSING];
else crossing=thatstation.s_ExitSide[TrainSide.CROSSING];
if (crossing < 0) { DError("Crossing isn't define yet",2); return -1; }
local goalTile=0;
if (direction==AIRail.RAILTRACK_NE_SW)
goalTile=AIMap.GetTileIndex(AIMap.GetTileX(crossing),AIMap.GetTileY(frontTile));
else goalTile=AIMap.GetTileIndex(AIMap.GetTileX(frontTile),AIMap.GetTileY(crossing));
return goalTile;
}
function cStationRail::RailStationDeleteEntrance(useEntry, stationID=null)
// Remove all tiles own by the station at its entry/exit
// useEntry: true to remove tiles for its entry, false for its exit
{
local thatstation=false;
if (stationID == null) { thatstation = this; }
else { thatstation = cStation.Load(stationID); }
if (!thatstation) { return -1; }
local removelist=AIList();
removelist.AddList(thatstation.s_TilesOther);
local value=0;
if (useEntry) { value=1; }
removelist.KeepValue(value);
cTrack.RailCleaner(removelist, thatstation.s_ID);
}
function cStationRail::StationAddTrain(taker, useEntry, stationID=null)
// Add a train to that station train counter
// stationID: the station ID
// taker: true if train is a taker, false if it's a dropper
// useEntry: true to use station entry, false for its exit
{
local thatstation=false;
if (stationID==null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) { DError("Invalid stationID "+stationID,1); return -1; }
local ted=thatstation.s_Train[TrainType.TED];
local txd=thatstation.s_Train[TrainType.TXD];
local tet=thatstation.s_Train[TrainType.TET];
local txt=thatstation.s_Train[TrainType.TXT];
if (taker)
{
if (useEntry) tet++;
else txt++;
}
else {
if (useEntry) ted++;
else txd++;
}
thatstation.s_Train[TrainType.TED]= ted;
thatstation.s_Train[TrainType.TXD]= txd;
thatstation.s_Train[TrainType.TET]= tet;
thatstation.s_Train[TrainType.TXT]= txt;
DInfo("Station "+cStation.GetStationName(thatstation.s_ID)+" add a new train: taker="+taker+" useEntry="+useEntry,2);
}
function cStationRail::StationRemoveTrain(taker, useEntry, stationID=null)
// Remove a train that use a station
{
local thatstation=false;
if (stationID==null) thatstation=this;
else thatstation=cStation.Load(stationID);
if (!thatstation) { DError("Invalid stationID "+stationID,1); return -1; }
local ted=thatstation.s_Train[TrainType.TED];
local txd=thatstation.s_Train[TrainType.TXD];
local tet=thatstation.s_Train[TrainType.TET];
local txt=thatstation.s_Train[TrainType.TXT];
if (taker)
{
if (useEntry) tet-=1;
else txt-=1;
}
else {
if (useEntry) ted-=1;
else txd-=1;
}
if (ted < 0) ted=0;
if (txd < 0) txd=0;
if (tet < 0) tet=0;
if (txt < 0) txt=0;
thatstation.s_Train[TrainType.TED]= ted;
thatstation.s_Train[TrainType.TXD]= txd;
thatstation.s_Train[TrainType.TET]= tet;
thatstation.s_Train[TrainType.TXT]= txt;
DInfo("Station "+cStation.GetStationName(thatstation.s_ID)+" remove train: taker="+taker+" useEntry="+useEntry,2);
}
function cStationRail::RailStationPhaseUpdate()
// Update platform and build missing parts
// thatstation is a cStationRail instance
{
local needUpdate=false;
if (this instanceof cStationRail)
{
this.DefinePlatform();
foreach (platform, status in this.s_Platforms)
{
if (this.s_EntrySide[TrainSide.CROSSING] >= 0) { needUpdate = true; cBuilder.PlatformConnectors(platform, true); }
if (this.s_ExitSide[TrainSide.CROSSING] >= 0) { needUpdate = true; cBuilder.PlatformConnectors(platform, false); }
}
}
else return;
if (needUpdate) this.DefinePlatform();
}
DictatorAI/class/cstationair.nut 0000644 0001750 0000144 00000002436 12203144534 016261 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cStationAir extends cStation
{
constructor()
{
::cStation.constructor();
this.ClassName = "cStationAir";
}
}
function cStation::IsStationVirtual(stationID)
// return true if the station is part of the airnetwork
{
return (cCarrier.VirtualAirRoute.len() > 1 && cStation.VirtualAirports.HasItem(stationID));
}
function cStationAir::CheckAirportLimits()
// Set limits for airports
{
if (!AIStation.IsValidStation(this.s_ID) || !AIStation.HasStationType(this.s_ID, AIStation.STATION_AIRPORT))
{
DWarn("Invalid airport station ID",1);
this.s_VehicleMax = 0;
return; // it happen if the airport is moved and now invalid
}
local rawlimit = INSTANCE.main.carrier.AirportTypeLimit[this.s_SubType];
DInfo("airport rawlimit="+rawlimit+" type="+this.s_SubType,1);
this.s_VehicleMax = rawlimit;
}
DictatorAI/class/cprocess.nut 0000644 0001750 0000144 00000026707 12203171216 015566 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
// I've learned a lot from rondje's code about squirrel, thank you guys !
class cProcess extends cClass
// Process are industries + towns
{
static database = {};
static statueTown = AIList(); // list of towns we use, for statues, decrease everytime a statue is there
UID = null; // id of industry/town
ID = null; // id of industry/town
Name = null; // name of industry/town
Location = null; // location of source
CargoAccept = null; // item=cargoID
CargoProduce = null; // item=cargoID, value=amount produce
IsTown = null; // True if it's a town
UpdateDate = null; // Date of last refresh
ScoreRating = null; // Score by rating for towns only
ScoreProduction = null; // Score by production
Score = null; // Total score
FailureDate = null; // Last date we have building a station and it has fail
IndustryType = null; // 1 - produce, 2- accept, 3- both
WaterAccess = null; // true if we can use a dock to access it
StationLocation = null; // this is the location of the station if one exist (dock/heliport)
constructor()
{
this.ClassName = "cProcess";
}
function GetProcessObject(UID) { return UID in cProcess.database ? cProcess.database[UID] : null; }
function Load(uid) {}
}
function cProcess::GetProcessList_ProducingCargo(cargoid)
// return an AIList of process that produce that cargo
{
local cargoList=AIIndustryList_CargoProducing(cargoid);
local townList=AITownList();
local cpass=cCargo.GetPassengerCargo();
local cmail=cCargo.GetMailCargo();
if ( (cpass != -1 && cpass == cargoid) || (cmail != -1 && cmail == cargoid) )
{
foreach (townID, _ in townList) cargoList.AddItem(townID+10000,0);
}
return cargoList;
}
function cProcess::GetProcessList_AcceptingCargo(cargoid)
// return an AIList of process that accept that cargo
{
local cargoList=AIIndustryList_CargoAccepting(cargoid);
local townList=AITownList();
local cpass=cCargo.GetPassengerCargo();
local cmail=cCargo.GetMailCargo();
if ( (cpass != -1 && cpass == cargoid) || (cmail != -1 && cmail == cargoid) )
{
foreach (townID, _ in townList) cargoList.AddItem(townID+10000,0);
}
return cargoList;
}
function cProcess::AddNewProcess(_id, _istown)
// Add a new process
{
local p = cProcess();
p.ID = _id;
p.IsTown = _istown;
p.UID = p.GetUID(_id, _istown);
local radius = AIStation.GetCoverageRadius(AIStation.STATION_DOCK);
if (_istown)
{
if (!AITown.IsValidTown(_id)) { return; }
p.Name = AITown.GetName(_id);
p.Location=AITown.GetLocation(_id);
if (AIMap.DistanceFromEdge(p.Location) < 50)
{
local tiles = cTileTools.GetTilesAroundPlace(p.Location, radius * 3);
tiles.Valuate(AITile.IsCoastTile);
tiles.KeepValue(1);
p.WaterAccess = (!tiles.IsEmpty());
}
}
else
{
if (!AIIndustry.IsValidIndustry(_id)) { return; }
p.Name=AIIndustry.GetName(_id);
p.Location=AIIndustry.GetLocation(_id);
if (AIIndustry.HasDock(_id)) { p.StationLocation = AIIndustry.GetDockLocation(_id); p.WaterAccess = true; }
if (AIIndustry.HasHeliport(_id)) { p.StationLocation = AIIndustry.GetHeliportLocation(_id); }
}
p.Name+="("+p.ID+")";
p.CargoProduce=AIList();
p.CargoAccept=AIList();
p.ScoreRating=1;
p.ScoreProduction=0;
p.CargoCheckSupply();
// must be done after cargo is set
if (!_istown && !p.WaterAccess && AIMap.DistanceFromEdge(p.Location) < 50)
{
local tiles = cTileTools.GetTilesAroundPlace(p.Location, (2* radius));
tiles.Valuate(AITile.IsCoastTile);
tiles.KeepValue(1);
local cargo = -1;
if (!p.CargoAccept.IsEmpty())
{ // we need 1 valid cargo, better check acceptance first then
cargo = p.CargoAccept.Begin();
tiles.Valuate(AITile.GetCargoAcceptance, cargo, 1, 1, radius);
tiles.KeepAboveValue(7);
}
else {
cargo = p.CargoProduce.Begin();
tiles.Valuate(AITile.GetCargoProduction, cargo, 1, 1, radius);
tiles.KeepAboveValue(0);
}
p.WaterAccess = !tiles.IsEmpty();
}
p.IndustryType = 0;
if (!p.CargoAccept.IsEmpty()) p.IndustryType += 2;
if (!p.CargoProduce.IsEmpty()) p.IndustryType += 1;
p.UpdateDate=null;
p.FailureDate = null;
p.UpdateScore();
p.Save();
}
function cProcess::Load(uid)
// Try to load a uid if need, throw error if it fail
{
local obj=cProcess.GetProcessObject(uid);
if (obj == null)
{
DWarn("cProcess.Load function return NULL with "+uid,1);
return false;
}
return obj;
}
function cProcess::GetProcessName(uid, istown)
// Return process name
{
local puid = cProcess.GetUID(uid, istown);
local p = cProcess.Load(puid);
if (!p) { return "Unknown name"; }
return p.Name;
}
function cProcess::DeleteProcess(uid=null)
// Remove a process
{
local obj=cProcess.Load(uid);
if (!obj) { return false; }
DInfo("Removing process #"+uid+" from database",2);
delete cProcess.database[uid];
}
// private
function cProcess::GetAmountOfCompetitorStationAround(IndustryID)
// Like AIIndustry::GetAmountOfStationAround but doesn't count our stations, so we only grab competitors stations
// return 0 or numbers of stations not own by us near the place
{
local counter=0;
local place = AIIndustry.GetLocation(IndustryID);
local radius = AIStation.GetCoverageRadius(AIStation.STATION_TRAIN);
local tiles = AITileList();
local produce = AITileList_IndustryAccepting(IndustryID, radius);
local accept = AITileList_IndustryProducing(IndustryID, radius);
tiles.AddList(produce);
tiles.AddList(accept);
tiles.Valuate(AITile.IsStationTile); // force keeping only station tile
tiles.KeepValue(1);
tiles.Valuate(AIStation.GetStationID);
local uniq = AIList();
foreach (i, dummy in tiles)
{ // remove duplicate id
if (!uniq.HasItem(dummy)) uniq.AddItem(dummy, i);
}
uniq.Valuate(AIStation.IsValidStation);
uniq.KeepValue(0); // remove our station tiles
return uniq.Count();
}
function cProcess::UpdateScoreRating()
// Update the Rating score
{
if (this.IsTown)
{
local rate = AITown.GetRating(this.ID, AICompany.ResolveCompanyID(AICompany.COMPANY_SELF));
if (rate == AITown.TOWN_RATING_NONE) { rate=AITown.TOWN_RATING_GOOD; }
if (rate < AITown.TOWN_RATING_POOR) { rate = 0; }
this.ScoreRating = 10 * rate;
}
else {
local competitor = cProcess.GetAmountOfCompetitorStationAround(this.ID);
local us = AIIndustry.GetAmountOfStationsAround(this.ID) - competitor;
switch (this.IndustryType)
{
case 1: // only produce
switch (INSTANCE.fairlevel)
{
case 0:
this.ScoreRating= 80 - (80 * competitor); // give up when 1 station is present
break;
case 1:
this.ScoreRating= 80 - (40 * competitor); // give up when 2
break;
case 2:
this.ScoreRating= 80 - (20 * competitor); // give up after 4
break;
}
break;
case 2: // only accept
this.ScoreRating = 80 - (competitor * 15); // at 6 it's crowd and free tiles get rare
this.ScoreRating = max(1, this.ScoreRating); // but even crowd, keep a little chance to consider it to drop cargo
break;
case 3: // both
this.ScoreRating = 80 - (competitor * 10);
this.ScoreRating += 50 * us; // give a great bonus if we are doing job there already
if (INSTANCE.fairlevel == 0) { this.ScoreRating = 0; } // Reserve the job for the poor user only
else { this.ScoreRating = max(1, this.ScoreRating); }
break;
}
}
}
function cProcess::UpdateScoreProduction()
// Update the production score
{
local best=0;
local bestcargo=-1;
local temp = AIList();
temp.AddList(this.CargoProduce);
foreach (cargoID, value in temp)
{
local current = 0;
if (this.IsTown) { current = AITown.GetLastMonthProduction(this.ID, cargoID); }
else {
current= AIIndustry.GetLastMonthProduction(this.ID, cargoID);
if (INSTANCE.fairlevel == 0) current -= AIIndustry.GetLastMonthTransported(this.ID, cargoID);
// reduce it for fair game setting
if (this.IndustryType == 2) { current = 1; } // force non-zero to allow only receiving industry get a rank > 0
}
if (best < current) { best=current; bestcargo=cargoID; }
this.CargoProduce.SetValue(cargoID, current);
}
if (bestcargo == cCargo.GetCargoFavorite()) { this.ScoreProduction = best * cCargo.GetCargoFavoriteBonus(); }
else { this.ScoreProduction = best; }
}
function cProcess::UpdateScore()
// Update score
{
if (this.UpdateDate != null && AIDate.GetCurrentDate() - this.UpdateDate < 7) { DInfo("Fresh score for "+this.Name,4); return false; }
this.UpdateScoreRating();
this.UpdateScoreProduction();
this.Score = this.ScoreRating * this.ScoreProduction;
if (this.Score < 0) { this.Score=0; }
local now = AIDate.GetCurrentDate();
this.UpdateDate = now;
if (this.FailureDate != null)
{
this.Score = 0;
if (now - this.FailureDate > 365) { this.FailureDate=null; }
}
DInfo("Update score for "+this.Name+" to "+this.Score+" Rating="+this.ScoreRating+" Prod="+this.ScoreProduction,3);
}
function cProcess::ZeroProcess()
// Set the process as bad
{
this.FailureDate = AIDate.GetCurrentDate();
this.UpdateDate = null;
this.ScoreRating = 0;
this.Score = 0;
}
function cProcess::CargoCheckSupply()
// Check and add cargo the process could handle
{
local type = 0;
if (this.IsTown)
{
local pass=cCargo.GetPassengerCargo();
if (AICargo.IsValidCargo(pass))
{
this.CargoAccept.AddItem(pass,0);
this.CargoProduce.AddItem(pass, 0);
}
local mail=cCargo.GetMailCargo();
if (AICargo.IsValidCargo(mail))
{
this.CargoAccept.AddItem(mail,0);
this.CargoProduce.AddItem(mail, 0);
}
type = 3;
}
else
{
this.CargoAccept=AICargoList_IndustryAccepting(this.ID);
local cargoList= AICargoList_IndustryProducing(this.ID);
foreach (cargo, _ in cargoList) this.CargoProduce.AddItem(cargo, 0);
}
}
function cProcess::Save()
// save the industry/town
{
if (this.UID in database)
{
DWarn("Process "+this.UID+" "+this.Name+" already in database",2);
}
else
{
DInfo("Adding process "+this.Score+"-"+this.Name+" to database",2);
cProcess.database[this.UID] <- this;
}
}
function cProcess::GetUID(_id, _istown)
// Create a UID for industry
{
local uID=_id;
if (typeof(uID) == "integer" && _istown) { uID+=10000; }
return uID;
}
DictatorAI/class/cprofiling.nut 0000755 0001750 0000144 00000002535 12051031607 016075 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cProfiling extends cClass
{
ticks = null;
date = null;
constructor()
{
ticks=AIController.GetTick();
date = AIDate.GetCurrentDate();
this.ClassName="cProfiling";
DInfo("Task start at "+ticks+" ticks, "+date,1);
}
}
function cProfiling::Stop()
{
local nticks=AIController.GetTick();
local ndate=AIDate.GetCurrentDate();
DInfo("Task end at "+nticks+", tooked "+(nticks-this.ticks)+" ticks, "+(ndate-this.date)+" days",1);
}
class cLooper extends cClass
// This class increase a counter, once value is met, it call AIController.Sleep.
// Creating this class inside a loop then gave a tiny break in the loop
{
static tickCounter = [0];
constructor()
{
this.ClassName = "cLooper";
local fetch = cLooper.tickCounter[0];
fetch++;
if (fetch > 200) { ::AIController.Sleep(1); fetch = 0; DInfo("Resting...",2); }
cLooper.tickCounter[0]=fetch;
}
}
DictatorAI/class/cdebug.nut 0000644 0001750 0000144 00000010754 12202216551 015172 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cDebug extends cClass
{
constructor() { this.ClassName="cDebug"; }
}
function cDebug::PutSign(place,msg)
// put a sign at place
{
if (!DictatorAI.GetSetting("debug_sign")) return;
if (place != null) AISign.BuildSign(place,msg.tostring());
}
function cDebug::ClearSigns()
// this just clear any signs we can
{
if (!DictatorAI.GetSetting("debug_sign")) return;
local sweeper=AISignList();
sweeper.Valuate(AISign.GetLocation);
sweeper.RemoveValue(INSTANCE.main.SCP.SCPTile);
AIController.Sleep(20);
foreach (i, dummy in sweeper) { AISign.RemoveSign(i); }
}
function cDebug::showLogic(item)
// this will draw sign with item so we see item influence
{
foreach (i, dummy in item)
{
cDebug.PutSign(i,dummy);
}
}
function cBuilder::DumpRoute(idx=null)
{
if (!INSTANCE.debug) return;
local road=null;
if (idx == null) road=INSTANCE.main.route;
else road=cRoute.Load(idx);
DInfo("Route "+road.Name+" VehicleType: "+cRoute.RouteTypeToString(road.VehicleType)+" status: "+road.Status+" Cargo:"+cCargo.GetCargoLabel(road.CargoID),2);
if (typeof(road.SourceStation) != "instance") return;
DInfo(road.SourceStation.s_Name+" Station type: "+road.SourceStation.s_Type+" subType: "+road.SourceStation.s_SubType+" produce "+road.SourceStation.s_CargoProduce.Count()+" cargos, accept "+road.SourceStation.s_CargoAccept.Count()+" cargos");
if (typeof(road.TargetStation) != "instance") return;
DInfo(road.TargetStation.s_Name+" Station type: "+road.TargetStation.s_Type+" subType: "+road.TargetStation.s_SubType+" produce "+road.TargetStation.s_CargoProduce.Count()+" cargos, accept "+road.TargetStation.s_CargoAccept.Count()+" cargos");
}
function cBuilder::DumpJobs(uid)
{
if (!INSTANCE.debug) return;
local tjob=cJobs.GetJobObject(uid);
local src=tjob.sourceObject.Name;
local dst=tjob.targetObject.Name;
DInfo("Jobs #"+uid+" "+src+"->"+dst+" Ranking="+tjob.ranking+" "+cCargo.GetCargoLabel(tjob.cargoID)+" value="+tjob.cargoValue+" cargo="+tjob.cargoAmount+" "+cRoute.RouteTypeToString(tjob.roadType)+" Cost: "+tjob.moneyToBuild+" doable? "+tjob.isdoable,1);
}
function cBuilder::DumpTopJobs()
{
if (!INSTANCE.debug) return;
local i=0;
foreach (uid, ranking in INSTANCE.main.jobs.jobDoable)
{
INSTANCE.main.builder.DumpJobs(uid);
if (i==12) break;
i++;
}
}
function cBuilder::ShowStationCapacity()
{
if (!INSTANCE.debug) return;
local stations=null;
local sta_list=AIStationList(AIStation.STATION_ANY);
if (!sta_list.IsEmpty()) foreach (sta_id, dummy in sta_list)
{
stations=cStation.Load(sta_id);
local stuck="CLOSE - ";
if (stations.CanUpgradeStation()) stuck="UPGRADE -";
local outtxt=stuck+stations.vehicle_count+" - "+stations.vehicle_max;
local outpos=stations.s_Location;
cDebug.PutSign(outpos,outtxt);
}
}
function cBuilder::ShowStationOwners()
{
if (!INSTANCE.debug) return;
local stations=null;
local sta_list=AIStationList(AIStation.STATION_ANY);
if (!sta_list.IsEmpty()) foreach (sta_id, dummy in sta_list)
{
stations=cStation.Load(sta_id);
if (!stations) continue;
if (stations.s_Owner.IsEmpty()) { cDebug.PutSign(stations.s_Location, "NOT OWN"); }
}
}
function cBuilder::ShowTrainStationDepot()
{
local alist=AIStationList(AIStation.STATION_TRAIN);
foreach (station, dummy in alist)
{
local thatstation=cStation.GetStationObject(station);
AISign.BuildSign(thatstation.depot,cStation.GetStationName(station));
}
}
function cBuilder::ShowBlackList()
{
foreach (tile, value in cTileTools.TilesBlackList) PutSign(tile,"BL:"+value);
}
function cBuilder::ShowPlatformStatus(stationID)
{
if (!INSTANCE.debug) return;
local station=cStation.GetStationObject(stationID);
foreach (platform, status in station.platform_entry)
if (status==1) PutSign(platform,"O");
else PutSign(platform,"X");
foreach (platform, status in station.platform_exit)
if (status==1) PutSign(platform,"O");
else PutSign(platform,"X");
}
function cDebug::ShowRailTrack()
{
local atrack = AIRailTypeList();
foreach (r, _ in atrack)
{
print("track #"+r+" name: "+AIRail.GetName(r)+" speed: "+AIRail.GetMaxSpeed(r));
}
}
DictatorAI/class/ctile.nut 0000755 0001750 0000144 00000072166 12205360276 015060 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cTileTools
{
static terraformCost = AIList();
static TilesBlackList = AIList(); // item=tile, value=stationID that own the tile
}
function cTileTools::IsTilesBlackList(tile)
{
return cTileTools.TilesBlackList.HasItem(tile);
}
function cTileTools::GetTileStationOwner(tile)
// return station that own the tile
// or -1
{
if (cTileTools.TilesBlackList.HasItem(tile)) { return cTileTools.TilesBlackList.GetValue(tile); }
return -1;
}
function cTileTools::CanUseTile(tile, owner)
// Answer if we can use that tile
{
local coulduse=true;
local ot=cTileTools.GetTileStationOwner(tile);
if (ot == owner) return true;
if (ot == -1) return true;
return false;
}
function cTileTools::BlackListTile(tile, stationID = -255)
{
// we store the stationID for a blacklisted tile or a negative value that tell us why it was blacklist
// -255 not usable at all, we can't use it
// -100 don't use that tile when building a station, it's a valid tile, but a bad spot
if (AIMap.IsValidTile(tile))
{
if (stationID == -255) { cTileTools.TilesBlackList.AddItem(tile, -255); return; }
if (stationID == -100) { cTileTools.TilesBlackList.AddItem(tile, -100); return; }
local owner = cTileTools.GetTileStationOwner(tile);
if (owner == -1) { cTileTools.TilesBlackList.AddItem(tile, stationID); return; }
// allow tile to be claim if not own by a station
if (owner < 0) { cTileTools.TilesBlackList.SetValue(tile, stationID); }
// allow temporary claims tiles (0 - 1000+stationID) to be reclaim by a real stationID
}
}
function cTileTools::UnBlackListTile(tile)
{
if (cTileTools.IsTilesBlackList(tile)) { cTileTools.TilesBlackList.RemoveItem(tile); }
}
function cTileTools::PurgeBlackListTiles(alist, creation=false)
// remove all tiles that are blacklist from an AIList and return it
// if creation is false, don't remove tiles that cannot be use for station creation
{
alist.Valuate(cTileTools.GetTileStationOwner);
alist.RemoveAboveValue(0); // remove own tiles
alist.RemoveValue(-255); // remove bad tiles
if (creation) { alist.RemoveValue(-100); } // remove bad spot for station
return alist;
}
function cTileTools::GetTilesAroundTown(town_id)
// Get tile around a town
{
local tiles = AITileList();
local townplace = AITown.GetLocation(town_id);
tiles=cTileTools.GetTilesAroundPlace(townplace,200);
tiles.Valuate(AITile.IsWithinTownInfluence, town_id);
tiles.KeepValue(1);
return tiles;
}
function cTileTools::FindStationTiles(tile)
// return a list of tiles where we have the station we found at tile
{
local stationid=AIStation.GetStationID(tile);
if (!AIStation.IsValidStation(stationid)) return AIList();
local tilelist=cTileTools.GetTilesAroundPlace(tile,16);
tilelist.Valuate(AIStation.GetStationID);
tilelist.KeepValue(stationid);
return tilelist;
}
function cTileTools::StationIsWithinTownInfluence(stationid, townid)
// A tweak for AIStation.IsWithinTownInfluence in openttd < 1.1.2
{
local stationtile=cTileTools.FindStationTiles(AIStation.GetLocation(stationid));
foreach (tile, dummy in stationtile) { if (AITile.IsWithinTownInfluence(tile, townid) || AITile.GetTownAuthority(tile) == townid) return true; }
return false;
}
function cTileTools::DemolishTile(tile)
// same as AITile.DemolishTile but retry after a little wait, protect rails, tunnel and bridge
{
/* if (AITile.IsStationTile(tile))
{
AISign.BuildSign(tile,"S");
AIController.Break("attacking station");
}*/
if (cTileTools.IsBuildable(tile)) return true;
if (AIRail.IsRailDepotTile(tile)) { return cTrack.StationKillRailDepot(tile); }
if (AIRoad.IsRoadDepotTile(tile) || AIMarine.IsWaterDepotTile(tile)) { return cTrack.DestroyDepot(tile); }
if (AIRail.IsRailTile(tile)) return false;
if (AIBridge.IsBridgeTile(tile) && cBridge.IsRailBridge(tile)) return false;
if (AITunnel.IsTunnelTile(tile)) return false;
local res = cError.ForceAction(AITile.DemolishTile, tile);
return res;
}
function cTileTools::GetTilesAroundPlace(place,maxsize)
// Get tiles around a place
{
local tiles = AITileList();
local mapSizeX = AIMap.GetMapSizeX();
local mapSizeY = AIMap.GetMapSizeY();
local ox = AIMap.GetTileX(place);
local oy = AIMap.GetTileY(place);
local tx = ox;
local ty = oy;
if (ox - maxsize < 1) ox = 1;
else ox = ox - maxsize;
if (tx + maxsize >= mapSizeX-2) tx = mapSizeX-2;
else tx = tx + maxsize;
if (oy - maxsize < 1) oy = 1;
else oy = oy - maxsize;
if (ty + maxsize >= mapSizeY-2) ty = mapSizeY-2;
else ty = ty + maxsize;
local o = AIMap.GetTileIndex(ox, oy);
local t = AIMap.GetTileIndex(tx, ty);
tiles.AddRectangle(o, t);
return tiles;
}
function cTileTools::IsBuildable(tile)
// function to check a water tile is buildable, handle non water with AITIle.IsBuildable()
{
if (AIMarine.IsDockTile(tile)) return false;
if (AIMarine.IsWaterDepotTile(tile)) return false;
if (AIMarine.IsBuoyTile(tile)) return false;
if (AIMarine.IsCanalTile(tile)) return false;
if (AIMarine.IsLockTile(tile)) return false;
if (!AITile.IsWaterTile(tile)) { return AITile.IsBuildable(tile); }
else return INSTANCE.terraform; // if no terraform is allow, water tile cannot be use
return AITile.IsBuildable(tile);
}
function cTileTools::IsAreaFlat(startTile, width, height)
// from nocab terraform.nut as Terraform::IsFlat
// http://www.tt-forums.net/viewtopic.php?f=65&t=43259&sid=9335e2ce38b4bd5e3d4df99cf23d30d7
{
local mapSizeX = AIMap.GetMapSizeX();
local goalHeight = AITile.GetMinHeight(startTile);
// Check if the terrain isn't already flat.
for (local i = 0; i < width; i++)
for (local j = 0; j < height; j++)
if (AITile.GetMinHeight(startTile + i + j * mapSizeX) != goalHeight ||
AITile.GetSlope(startTile + i + j * mapSizeX) != AITile.SLOPE_FLAT)
return false;
return true;
}
function cTileTools::IsBuildableRectangleFlat(tile, width, height, ignoreList=AIList())
// a wrapper to AITile.IsBuildableRectangle that also answer to "Are all tiles flat"
{
local check=AITile.IsBuildableRectangle(tile, width, height);
if (!check) return false;
return cTileTools.IsAreaFlat(tile, width, height);
}
function cTileTools::IsBuildableRectangle(tile, width, height, ignoreList=AIList())
// This check if the rectangle area is buildable in any directions from that point
// Like the IsBuildableRectangle, but not limit to upper left point
// tile : tile to search a solve for
// width & height : dimensions
// ignoreList: AIList() with tiles we should ignore (report them as if they were IsBuildable even something is there)
// return tile point where to put the objet or -1 if nothing is buildable there
{
local returntile=-1;
local tilelist=AITileList();
local secondtile=0;
local before=0;
local after=0;
//DInfo("Width: "+width+" height: "+height,1,"IsBuildableRectangle");
width-=1;
height-=1;
if (width < 0) width=0;
if (height < 0) height=0;
// tile is @ topleft of the rectangle
// secondtile is @ lowerright
tilelist.Clear();
secondtile=tile+AIMap.GetTileIndex(width,height);
returntile=tile;
tilelist.AddRectangle(tile,secondtile);
tilelist.Valuate(cTileTools.IsBuildable);
before=tilelist.Count();
foreach (itile, idummy in ignoreList)
{
if (tilelist.HasItem(itile)) tilelist.SetValue(itile,1);
}
tilelist.KeepValue(1);
after=tilelist.Count();
if (after==before)
{
//if (INSTANCE.debug) foreach (tile, dummy in tilelist) cDebug.PutSign(tile,"1");
//cDebug.PutSign(returntile,"X");
return returntile;
}
// tile is @ topright of the rectangle
// secondtile is @ lowerleft
tilelist.Clear();
secondtile=tile+AIMap.GetTileIndex(width,0-height);
returntile=tile+AIMap.GetTileIndex(0,0-height);
tilelist.AddRectangle(tile,secondtile);
tilelist.Valuate(cTileTools.IsBuildable);
before=tilelist.Count();
foreach (itile, idummy in ignoreList)
{
if (tilelist.HasItem(itile)) tilelist.SetValue(itile,1);
}
tilelist.KeepValue(1);
after=tilelist.Count();
if (after==before)
{
//if (INSTANCE.debug) foreach (tile, dummy in tilelist) cDebug.PutSign(tile,"2");
//cDebug.PutSign(returntile,"X");
return returntile;
}
// tile is @ lowerleft of the rectangle
// secondtile is @ upperright
tilelist.Clear();
secondtile=tile+AIMap.GetTileIndex(0-width,height);
returntile=tile+AIMap.GetTileIndex(0-width,0);
tilelist.AddRectangle(tile,secondtile);
tilelist.Valuate(cTileTools.IsBuildable);
before=tilelist.Count();
foreach (itile, idummy in ignoreList)
{
if (tilelist.HasItem(itile)) tilelist.SetValue(itile,1);
}
tilelist.KeepValue(1);
after=tilelist.Count();
if (after==before)
{
//if (INSTANCE.debug) foreach (tile, dummy in tilelist) cDebug.PutSign(tile,"3");
//cDebug.PutSign(returntile,"X");
return returntile;
}
// tile is @ lowerright of the rectangle
// secondtile is @ upperleft
tilelist.Clear();
secondtile=tile+AIMap.GetTileIndex(0-width,0-height);
returntile=secondtile;
tilelist.AddRectangle(tile,secondtile);
tilelist.Valuate(cTileTools.IsBuildable);
before=tilelist.Count();
foreach (itile, idummy in ignoreList)
{
if (tilelist.HasItem(itile)) tilelist.SetValue(itile,1);
}
tilelist.KeepValue(1);
after=tilelist.Count();
if (after==before)
{
//if (INSTANCE.debug) foreach (tile, dummy in tilelist) cDebug.PutSign(tile,"4");
//cDebug.PutSign(returntile,"X");
return returntile;
}
return -1;
}
function cTileTools::IsFlatBuildableAreaExist(tile, width, height, ignoreList=AIList())
// This check if the rectangle area is buildable and flat in any directions from that point
// Like the IsBuildableRectangle, but not limit to upper left point
// tile : tile to search a solve for
// width & height : dimensions
// ignoreList: AIList() with tiles we should ignore (report them as if they were IsBuildable even something is there)
// return tile point where to put the objet or -1 if nothing is buildable there
{
local returntile=-1;
local tilelist=AITileList();
local secondtile=0;
local before=0;
local after=0;
//DInfo("Width: "+width+" height: "+height,1,"IsBuildableRectangle");
width-=1;
height-=1;
if (width < 0) width=0;
if (height < 0) height=0;
// tile is @ topleft of the rectangle
// secondtile is @ lowerright
tilelist.Clear();
secondtile=tile+AIMap.GetTileIndex(width,height);
returntile=tile;
tilelist.AddRectangle(tile,secondtile);
tilelist.Valuate(cTileTools.IsBuildable);
before=tilelist.Count();
foreach (itile, idummy in ignoreList)
{
if (tilelist.HasItem(itile)) tilelist.SetValue(itile,1);
}
tilelist.KeepValue(1);
//if (INSTANCE.debug) foreach (tile, dummy in tilelist) cDebug.PutSign(tile,"1");
after=tilelist.Count();
if (after==before && cTileTools.IsAreaFlat(returntile, width+1, height+1)) return returntile;
// tile is @ topright of the rectangle
// secondtile is @ lowerleft
tilelist.Clear();
secondtile=tile+AIMap.GetTileIndex(width,0-height);
returntile=tile+AIMap.GetTileIndex(0,0-height);
tilelist.AddRectangle(tile,secondtile);
tilelist.Valuate(cTileTools.IsBuildable);
before=tilelist.Count();
foreach (itile, idummy in ignoreList)
{
if (tilelist.HasItem(itile)) tilelist.SetValue(itile,1);
}
tilelist.KeepValue(1);
//if (INSTANCE.debug) foreach (tile, dummy in tilelist) cDebug.PutSign(tile,"2");
after=tilelist.Count();
if (after==before && cTileTools.IsAreaFlat(returntile, width+1, height+1)) return returntile;
// tile is @ lowerleft of the rectangle
// secondtile is @ upperright
tilelist.Clear();
secondtile=tile+AIMap.GetTileIndex(0-width,height);
returntile=tile+AIMap.GetTileIndex(0-width,0);
tilelist.AddRectangle(tile,secondtile);
tilelist.Valuate(cTileTools.IsBuildable);
before=tilelist.Count();
foreach (itile, idummy in ignoreList)
{
if (tilelist.HasItem(itile)) tilelist.SetValue(itile,1);
}
tilelist.KeepValue(1);
//if (INSTANCE.debug) foreach (tile, dummy in tilelist) cDebug.PutSign(tile,"3");
after=tilelist.Count();
if (after==before && cTileTools.IsAreaFlat(returntile, width+1, height+1)) return returntile;
// tile is @ lowerright of the rectangle
// secondtile is @ upperleft
tilelist.Clear();
secondtile=tile+AIMap.GetTileIndex(0-width,0-height);
returntile=secondtile;
tilelist.AddRectangle(tile,secondtile);
tilelist.Valuate(cTileTools.IsBuildable);
before=tilelist.Count();
foreach (itile, idummy in ignoreList)
{
if (tilelist.HasItem(itile)) tilelist.SetValue(itile,1);
}
tilelist.KeepValue(1);
after=tilelist.Count();
//if (INSTANCE.debug) foreach (tile, dummy in tilelist) cDebug.PutSign(tile,"4");
if (after==before && cTileTools.IsAreaFlat(returntile, width+1, height+1)) return returntile;
return -1;
}
function cTileTools::ShapeTile(tile, wantedHeight, evaluateOnly)
// Flatten the tile at wanted height level
// tile: tile to shape
// wantedHeight: height to flatten land to
// evaluateOnly: ignore steep slope : that's to not fail when we evaluate the success
{
local srcL=AITile.GetMinHeight(tile);
local srcH=AITile.GetMaxHeight(tile);
local slope=AITile.GetSlope(tile);
local error=null;
local generror=false;
local tsign=null;
local compSlope=AITile.GetComplementSlope(slope);
if (srcL == wantedHeight && srcH == wantedHeight) return generror;
if (!INSTANCE.terraform)
{
DInfo("AI terraforming is disable, failure",1);
return true;
}
do {
srcL=AITile.GetMinHeight(tile);
srcH=AITile.GetMaxHeight(tile);
slope=AITile.GetSlope(tile);
compSlope=AITile.GetComplementSlope(slope);
if ((slope & AITile.SLOPE_STEEP) == AITile.SLOPE_STEEP)
{
slope-=AITile.SLOPE_STEEP;
compSlope=AITile.GetComplementSlope(slope);
//DInfo("Tile: "+tile+" Removing SteepSlope",2);
AITile.RaiseTile(tile,compSlope);
error=AIError.GetLastError();
if (error != AIError.ERR_NONE) generror=true;
slope=AITile.GetSlope(tile);
compSlope=AITile.GetComplementSlope(slope);
srcL=AITile.GetMinHeight(tile);
srcH=AITile.GetMaxHeight(tile);
if (evaluateOnly) return false;
}
//DInfo("Tile: "+tile+" Slope: "+slope+" compSlope: "+compSlope+" target: "+wantedHeight+" srcL: "+srcL+" srcH: "+srcH+" real slope: "+AITile.GetSlope(tile),2,"ShapeTile");
cDebug.PutSign(tile,"!");
AIController.Sleep(1);
if ((srcH < wantedHeight || srcL < wantedHeight) && !generror)
{
if (AITile.GetSlope(tile) == AITile.SLOPE_ELEVATED)
AITile.RaiseTile(tile, AITile.SLOPE_FLAT);
else AITile.RaiseTile(tile, compSlope);
error=AIError.GetLastError();
//DInfo("Raising tile "+AIError.GetLastErrorString()+" minHeight: "+AITile.GetMinHeight(tile)+" maxheight: "+AITile.GetMaxHeight(tile)+" new slope:"+AITile.GetSlope(tile),2,"ShapeTile");
if (error != AIError.ERR_NONE) generror=true;
}
if ((srcL > wantedHeight || srcH > wantedHeight) && !generror)
{
if (AITile.GetSlope(tile) == AITile.SLOPE_FLAT)
{ AITile.LowerTile(tile, AITile.SLOPE_ELEVATED);}
else { AITile.LowerTile(tile, slope); }
error=AIError.GetLastError();
//DInfo("Lowering tile "+AIError.GetLastErrorString()+" minHeight: "+AITile.GetMinHeight(tile)+" maxheight: "+AITile.GetMaxHeight(tile)+" new slope:"+AITile.GetSlope(tile),2,"ShapeTile");
if (error != AIError.ERR_NONE) generror=true;
}
} while (!generror && srcH != wantedHeight && srcL != wantedHeight && !evaluateOnly);
return generror;
}
function cTileTools::MostItemInList(list, item)
// add item to list if not exist and set counter to 1, else increase counter
// return the list
{
if (!list.HasItem(item)) { list.AddItem(item,1); }
else { local c=list.GetValue(item); c++; list.SetValue(item,c); }
return list;
}
function cTileTools::CheckLandForConstruction(tile, width, height, ignoreList=AIList())
// Check the tiles area for construction, look if tiles are clear, and flatten the land if need
// return -1 on failure, on success the tile where to drop a construction (upper left tile)
{
cDebug.PutSign(tile,"?");
local newTile=cTileTools.IsBuildableRectangle(tile, width, height, ignoreList);
if (newTile == -1) return newTile; // area not clear give up, the terraforming will fail too
if (cTileTools.IsBuildableRectangleFlat(newTile, width, height)) return newTile;
local tileTo=newTile+AIMap.GetTileIndex(width-1,height-1);
INSTANCE.main.bank.RaiseFundsBigTime();
cTileTools.TerraformLevelTiles(newTile, tileTo);
INSTANCE.NeedDelay(20);
if (cTileTools.IsBuildableRectangleFlat(newTile, width, height)) return newTile;
return -1;
}
function cTileTools::TerraformLevelTiles(tileFrom, tileTo)
// terraform from tileFrom to tileTo
// return true if success
{
local tlist=AITileList();
tlist.AddRectangle(tileFrom, tileTo);
if (tlist.IsEmpty()) DInfo("No tiles to work with !",4);
local Solve=cTileTools.TerraformHeightSolver(tlist);
Solve.RemoveValue(0); // discard failures
local bestOrder=AIList();
bestOrder.AddList(Solve);
foreach (level, prize in Solve)
{
local c=abs(prize);
bestOrder.SetValue(level,prize);
}
bestOrder.Sort(AIList.SORT_BY_VALUE, true);
local money = -1;
foreach (solution, prize in bestOrder) DInfo("solve: "+solution+" prize: "+prize,4);
if (!Solve.IsEmpty())
{
foreach (solution, prize in bestOrder)
{
local direction=Solve.GetValue(solution);
if (!cBanker.CanBuyThat(prize))
{
DInfo("Stopping action. We won't have enought money to succeed",4);
cTileTools.terraformCost.AddItem(999999,prize);
break;
}
cBanker.RaiseFundsBigTime();
if (direction < 0) money=cTileTools.TerraformDoAction(tlist, solution, true, false);
else money=cTileTools.TerraformDoAction(tlist, solution, false, false);
if (money != -1)
{
DInfo("Success, we spent "+money+" credits for the operation",4);
return true;
}
}
}
DInfo("Fail",3);
return false;
}
function cTileTools::TerraformDoAction(tlist, wantedHeight, UpOrDown, evaluate=false)
// Try flatten tiles in tlist to the wanted height level
// tlist : tiles to check
// wantedHeight : the level to flatten tiles to
// UpOrDown: true to level down, false to level up the tiles
// evaluate : true to only check if we can do it, else we will execute the terraforming
// return : -1 in both case if this will fail
// when evaluate & success return estimated costs need to do the operation
// when !evaluate & success return costs taken to do the operation
{
local moneySpend=0;
local moneyNeed=0;
local tTile=AITileList();
tTile.AddList(tlist);
tTile.Sort(AIList.SORT_BY_VALUE,UpOrDown);
local costs=AIAccounting();
local testrun=AITestMode();
local error=false;
foreach (tile, max in tTile)
{
error=cTileTools.ShapeTile(tile, wantedHeight, true);
if (error) break;
}
testrun=null;
moneyNeed=costs.GetCosts();
DInfo("predict failure : "+error+" Money need="+moneyNeed,4);
if (error) moneyNeed=-1;
if (evaluate) return moneyNeed;
costs.ResetCosts();
if (!error)
{
foreach (tile, max in tTile)
{
error=cTileTools.ShapeTile(tile, wantedHeight, false);
if (error) break;
}
}
moneySpend=costs.GetCosts();
DInfo("spent "+moneySpend+" money",3);
if (!error)
{
DInfo("flatten successfuly land at level : "+wantedHeight,4);
return moneySpend;
}
DInfo("fail flatten land at level : "+wantedHeight,4);
return -1;
}
function cTileTools::TerraformHeightSolver(tlist)
// Look at tiles in tlist and try to find the height that cost us the less to flatten them all at same height
// tlist: the tile list to check
// return : tilelist table with item=height
// value = 0 when failure
// value > 0 should success if raising tiles, it's also money we need to do it
// value < 0 should success if lowering tiles, it's also the negative value of money need to do it
// so best solve is lowest value (by abs value) && value != 0
{
if (tlist.IsEmpty())
{
DInfo("doesn't find any tiles to work on!",4);
return AIList();
}
local maxH=tlist;
local minH=AITileList();
local moneySpend=0;
local moneyNeed=0;
minH.AddList(maxH);
maxH.Valuate(AITile.GetMaxHeight);
minH.Valuate(AITile.GetMinHeight);
local cellHCount=AIList();
local cellLCount=AIList();
foreach (tile, max in maxH)
{
// this loop count each tile lower height and higher height on each tiles
cellHCount=cTileTools.MostItemInList(cellHCount,maxH.GetValue(tile)); // could use "max" var instead, but clearer as-is
cellLCount=cTileTools.MostItemInList(cellLCount,minH.GetValue(tile));
}
cellHCount.Sort(AIList.SORT_BY_VALUE,false);
cellLCount.Sort(AIList.SORT_BY_VALUE,false);
local HeightIsLow=true;
local currentHeight=-1;
local h_firstitem=1000;
local l_firstitem=1000;
local Solve=AIList();
local terratrys=0;
DInfo("Start near "+AITown.GetName(AITile.GetClosestTown(tlist.Begin())),3);
do {
h_firstitem=cellHCount.Begin();
l_firstitem=cellLCount.Begin();
if (cellHCount.GetValue(h_firstitem) < cellLCount.GetValue(l_firstitem))
{
HeightIsLow=true;
currentHeight=l_firstitem;
cellLCount.RemoveItem(l_firstitem);
DInfo("Trying lowering tiles level to "+currentHeight,4);
}
else {
HeightIsLow=false;
currentHeight=h_firstitem;
cellHCount.RemoveItem(h_firstitem);
DInfo("Trying raising tiles level to "+currentHeight,4);
}
// Now we have determine what low or high height we need to reach by priority (most tiles first, with a pref to lower height)
terratrys++;
if (currentHeight == 0) // not serious to build at that level
{
DInfo("Water level detect !",4);
Solve.AddItem(0,0);
continue;
}
local money=0;
local error=false;
money=cTileTools.TerraformDoAction(maxH, currentHeight, HeightIsLow, true);
if (money != -1)
{
DInfo("Solve found, "+money+" credits need to reach level "+currentHeight,4);
if (money == 0) money=1; // in case no money is need, we still ask 1 credit, else we will mistake as a failure
if (HeightIsLow) money=0-money; // force negative to lower tile
}
else money=0; // 0 == failure
if (Solve.HasItem(currentHeight) && money!=0)
{
if (abs(Solve.GetValue(currentHeight)) > abs(money)) Solve.SetValue(currentHeight,money); // add it if solve cost less
}
else Solve.AddItem(currentHeight,money);
} while (cellHCount.Count() > 0 && cellLCount.Count() > 0); // loop until both lists are empty
DInfo("Solver has search "+terratrys+" time",4);
return Solve;
}
function cTileTools::TownBriber(townID)
// Bribe a town upto getting the neededRating
{
if (AITown.IsActionAvailable(townID, AITown.TOWN_ACTION_BRIBE))
{
DInfo("Offering money to "+AITown.GetName(townID),1);
return AITown.PerformTownAction(townID, AITown.TOWN_ACTION_BRIBE);
}
else return false;
}
function cTileTools::PlantsTreeAtTown(townID, makeplace=false)
// Plants tree near townID to improve rating
// Return true if we found any free tiles to work on
{
local towntiles = cTileTools.GetTilesAroundPlace(AITown.GetLocation(townID), 200);
towntiles.Valuate(AITile.IsBuildable)
towntiles.KeepValue(1);
towntiles.Valuate(AITile.GetTownAuthority);
towntiles.KeepValue(townID);
towntiles.Valuate(AITile.HasTreeOnTile);
if (makeplace)
{
towntiles.KeepValue(1);
foreach (tiles, _ in towntiles) if (!AITile.DemolishTile(tiles)) return false;
return true;
}
towntiles.KeepValue(0);
foreach (tiles, _ in towntiles) AITile.PlantTree(tiles);
return (!towntiles.IsEmpty());
}
function cTileTools::TownRatingNice(townID)
// return true if our rating with that town is at least mediocre
{
local curRating = AITown.GetRating(townID, AICompany.ResolveCompanyID(AICompany.COMPANY_SELF));
if (curRating == AITown.TOWN_RATING_NONE) curRating = AITown.TOWN_RATING_GOOD;
return (curRating >= AITown.TOWN_RATING_MEDIOCRE);
}
function cTileTools::SeduceTown(townID)
// Try seduce a town
// needRating : rating we must reach
// return true if we reach needRating level with that town
{
local weare=AICompany.ResolveCompanyID(AICompany.COMPANY_SELF);
local curRating = AITown.GetRating(townID, weare);
local town_name=AITown.GetName(townID);
DInfo("Town: "+town_name+" rating: "+curRating,2);
if (curRating == AITown.TOWN_RATING_NONE) curRating = AITown.TOWN_RATING_GOOD;
// plants tree to improve our rating to a town that doesn't know us yet
if (curRating >= AITown.TOWN_RATING_POOR)
{
DInfo("Could get costy for no result to continue seducing "+town_name+", giving up.",1);
return true;
}
local keeploop=true;
if (curRating == AITown.TOWN_RATING_APPALLING) cTileTools.PlantsTreeAtTown(townID, true);
// clear any trees place to rebuild them later
DInfo( "Trying bribing "+town_name+" as much as we can",1);
do {
keeploop=(AITown.GetRating(townID, weare) < AITown.TOWN_RATING_POOR);
if (keeploop) keeploop=cTileTools.TownBriber(townID);
if (!keeploop) DInfo("Result ="+keeploop,2);
::AIController.Sleep(10);
} while (keeploop);
// bad bribe will put us at POOR on failure, goog to keep bribing until we fail then
if (!cTileTools.PlantsTreeAtTown(townID)) DInfo("Cannot seduce "+town_name+" anymore with tree.",1);
return (AITown.GetRating(townID, weare) >= AITown.TOWN_RATING_POOR);
}
function cTileTools::IsRemovable(tile)
// return true/false if the tile could be remove
{
local test=false;
if (cTileTools.IsBuildable(tile)) return true;
local testmode=AITestMode();
test=cTileTools.DemolishTile(tile);
testmode=null;
return test;
}
function cTileTools::IsAreaRemovable(area)
// return true/false is all tiles could be remove in area AIList
{
local worklist=AIList();
worklist.AddList(area); // protect area list values
cTileTools.YexoValuate(worklist, cTileTools.IsRemovable);
worklist.RemoveValue(1);
return (worklist.IsEmpty());
}
function cTileTools::ClearArea(area)
// Clean out an area to allow building on it
{
foreach (tile, _ in area) cTileTools.DemolishTile(tile);
}
function cTileTools::IsAreaBuildable(area)
// return true if owner can destroy to build the area
{
if (!cTileTools.IsAreaRemovable(area)) return false;
local owncheck=AIList();
owncheck.AddList(area);
owncheck.Valuate(AITile.IsBuildable);
owncheck.KeepValue(0); // keep only non useable tiles
if (owncheck.Count() > 5) return false;
return true;
}
// This function comes from AdmiralAI, version 22, written by Yexo
// taken from SuperLib, this will becomes the most re-use function :D
function cTileTools::YexoCallFunction(func, args)
{
switch (args.len()) {
case 0: return func();
case 1: return func(args[0]);
case 2: return func(args[0], args[1]);
case 3: return func(args[0], args[1], args[2]);
case 4: return func(args[0], args[1], args[2], args[3]);
case 5: return func(args[0], args[1], args[2], args[3], args[4]);
case 6: return func(args[0], args[1], args[2], args[3], args[4], args[5]);
case 7: return func(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
case 8: return func(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
default: throw "Too many arguments to CallFunction";
}
}
// This function comes from AdmiralAI, version 22, written by Yexo
// taken from SuperLib, this will becomes the most re-use function :D
function cTileTools::YexoValuate(list, valuator, ...)
{
assert(typeof(list) == "instance");
assert(typeof(valuator) == "function");
local args = [null];
for(local c = 0; c < vargc; c++) {
args.append(vargv[c]);
}
foreach(item, _ in list) {
args[0] = item;
local value = cTileTools.YexoCallFunction(valuator, args);
if (typeof(value) == "bool") {
value = value ? 1 : 0;
} else if (typeof(value) != "integer") {
throw("Invalid return type from valuator");
}
list.SetValue(item, value);
}
}
function cTileTools::GetPosRelativeFromDirection(dirswitch, direction)
// Get the relative tile from "dirswitch" relative to "direction"
// dirswitch: 0- left, 1-right, 2-forward, 3=backward
{
local left, right, forward, backward = null;
switch (direction)
{
case DIR_NE:
left=AIMap.GetTileIndex(0,-1);
right=AIMap.GetTileIndex(0,1);
forward=AIMap.GetTileIndex(-1,0);
backward=AIMap.GetTileIndex(1,0);
break;
case DIR_SE:
left=AIMap.GetTileIndex(-1,0);
right=AIMap.GetTileIndex(1,0);
forward=AIMap.GetTileIndex(0,1);
backward=AIMap.GetTileIndex(0,-1);
break;
case DIR_SW:
left=AIMap.GetTileIndex(0,1);
right=AIMap.GetTileIndex(0,-1);
forward=AIMap.GetTileIndex(1,0);
backward=AIMap.GetTileIndex(-1,0);
break;
case DIR_NW:
left=AIMap.GetTileIndex(1,0);
right=AIMap.GetTileIndex(-1,0);
forward=AIMap.GetTileIndex(0,-1);
backward=AIMap.GetTileIndex(0,1);
break;
}
switch (dirswitch)
{
case 0:
return left;
case 1:
return right;
case 2:
return forward;
case 3:
return backward;
}
return -1;
}
function cTileTools::GetLeftRelativeFromDirection(direction)
{
return cTileTools.GetPosRelativeFromDirection(0,direction);
}
function cTileTools::GetRightRelativeFromDirection(direction)
{
return cTileTools.GetPosRelativeFromDirection(1,direction);
}
function cTileTools::GetForwardRelativeFromDirection(direction)
{
return cTileTools.GetPosRelativeFromDirection(2,direction);
}
function cTileTools::GetBackwardRelativeFromDirection(direction)
{
return cTileTools.GetPosRelativeFromDirection(3,direction);
}
function cTileTools::GetDistanceChebyshevToTile(tilefrom, tileto)
{
local x1 = AIMap.GetTileX(tilefrom);
local x2 = AIMap.GetTileX(tileto);
local y1 = AIMap.GetTileY(tilefrom);
local y2 = AIMap.GetTileY(tileto);
return max(abs(x2 - x1), abs(y2 - y1));
}
DictatorAI/class/cvehicle.nut 0000777 0001750 0000144 00000007562 12200770710 015535 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cCarrier extends cClass
{
static AirportTypeLimit=[6, 10, 2, 16, 30, 5, 6, 60, 8]; // limit per airport type
static IDX_HELPER = 512; // use to create an uniq ID (also use to set handicap value)
static AIR_NET_CONNECTOR=2500; // town is add to air network when it reach that value population
static ToDepotList=AIList(); // list vehicle going to depot, value=DepotAction for trains we also add wagons need
static vehicle_database={}; // database for vehicles
static VirtualAirRoute=[]; // the air network destinations list
static OldVehicle=1095; // age left we consider a vehicle is old
static MaintenancePool=[]; // list of vehicles that need maintenance
static function GetVehicleObject(vehicleID)
{
return vehicleID in cCarrier.vehicle_database ? cCarrier.vehicle_database[vehicleID] : null;
}
rail_max =null; // maximum trains vehicle a station can handle
road_max =null; // maximum a road station can upgarde (max: 6)
road_upgrade =null; // maximum vehicle a road station can support before upgarde itself
air_max =null; // maximum aircraft a station can handle
airnet_max =null; // maximum aircraft on a network
airnet_count =null; // current number of aircrafts running the network
water_max =null; // maximum ships a station can handle
road_max_onroute=null; // maximum road vehicle on a route
train_length =null; // maximum length for train/rail station
vehicle_cash =null; // amount of money we need to buy vehicle
do_profit =null; // Record each vehicle profits
warTreasure =null; // total current value of nearly all our road vehicle
highcostAircraft=null; // the highest cost for an aircraft we need
highcostTrain =null; // the highest cost for a train
speed_MaxTrain =null; // maximum speed a train could do
speed_MaxRoad =null; // maximum speed a road vehicle could do
running_vehicle =null; // number of vehicle per type we own
vehicle_wishlist=null; // our list of engines to buy
constructor()
{
this.ClassName="cCarrier";
rail_max = 0;
road_max = 0;
road_upgrade = 0;
air_max = 0;
airnet_max = 0;
airnet_count = 0;
water_max = 0;
road_max_onroute= 0;
train_length = 0;
vehicle_cash = 0;
do_profit = AIList();
warTreasure = 0;
highcostAircraft= 0;
highcostTrain = 0;
speed_MaxTrain = 0;
speed_MaxRoad = 0;
running_vehicle = [0,0,0,0];
vehicle_wishlist= AIList();
}
}
function cCarrier::GetVehicleName(veh)
// return a vehicle string with the vehicle infos
{
if (!AIVehicle.IsValidVehicle(veh)) return " #"+veh;
local toret="#"+veh+" "+AIVehicle.GetName(veh)+"("+cEngine.GetName(AIVehicle.GetEngineType(veh))+")";
return toret;
}
function cCarrier::GetVehicleCount(vehtype)
// return number of vehicle we own
// return 0 on error
{
return INSTANCE.main.carrier.running_vehicle[vehtype];
}
function cCarrier::VehicleCountUpdate()
// update the vehicle counter for vehtype
{
local allvehlist=AIVehicleList();
allvehlist.Valuate(AIVehicle.GetVehicleType);
local ro=0, tr=0, sh=0, ai=0;
foreach (veh, vtype in allvehlist)
{
switch (vtype)
{
case AIVehicle.VT_RAIL:
tr++;
break;
case AIVehicle.VT_ROAD:
ro++;
break;
case AIVehicle.VT_WATER:
sh++;
break;
case AIVehicle.VT_AIR:
ai++;
break;
}
}
running_vehicle[AIVehicle.VT_RAIL]=tr;
running_vehicle[AIVehicle.VT_ROAD]=ro;
running_vehicle[AIVehicle.VT_WATER]=sh;
running_vehicle[AIVehicle.VT_AIR]=ai;
}
DictatorAI/class/cevents.nut 0000755 0001750 0000144 00000012067 12203035730 015411 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cEvents extends cClass
{
eventList = null;
event = null;
constructor() {
this.ClassName="cEvents";
eventList = [];
event=0;
}
}
function cEvents::HandleEvents()
{
while (AIEventController.IsEventWaiting())
{
local event=AIEventController.GetNextEvent();
local eventType=event.GetEventType();
DInfo("New event incoming: "+eventType,2);
switch (eventType)
{
case AIEvent.ET_TOWN_FOUNDED:
event = AIEventTownFounded.Convert(event);
local town = event.GetTownID();
DInfo("New town found ! "+AITown.GetName(town),0);
cProcess.AddNewProcess(town, true);
cJobs.AddNewIndustryOrTown(town, true); // instant add, we need that info asap
break;
case AIEvent.ET_COMPANY_BANKRUPT:
// in case someone disapears, nothing, we will catch problems for sure
break;
case AIEvent.ET_INDUSTRY_OPEN:
event = AIEventIndustryOpen.Convert(event);
local industry = event.GetIndustryID();
DInfo("New industry "+AIIndustry.GetName(industry),0);
cProcess.AddNewProcess(industry, false)
cJobs.RawJob_Add(industry); // queue it to be process later
break;
case AIEvent.ET_INDUSTRY_CLOSE:
event = AIEventIndustryClose.Convert(event);
local industry = event.GetIndustryID();
DInfo("Industry "+AIIndustry.GetName(industry)+" is closing !",0);
cJobs.MarkIndustryDead(industry); // remove any job ref
break;
case AIEvent.ET_COMPANY_NEW:
event = AIEventCompanyNew.Convert(event);
local company = AICompany.GetName(event.GetCompanyID());
DInfo("Welcome "+company,0);
break;
case AIEvent.ET_ENGINE_PREVIEW:
event = AIEventEnginePreview.Convert(event);
if (event.AcceptPreview())
{
DInfo("New engine available for preview: " + event.GetName(),0);
}
INSTANCE.main.carrier.CheckOneVehicleOfGroup(true);
break;
case AIEvent.ET_ENGINE_AVAILABLE:
event = AIEventEngineAvailable.Convert(event);
local engine = event.GetEngineID();
DInfo("New engine available: " + cEngine.GetName(engine),0);
INSTANCE.main.carrier.CheckOneVehicleOfGroup(true);
break;
case AIEvent.ET_VEHICLE_CRASHED:
local vehicle = null;
event = AIEventVehicleCrashed.Convert(event);
vehicle = event.GetVehicleID();
DInfo("Vehicle "+INSTANCE.main.carrier.GetVehicleName(vehicle)+" has crashed!!!",0);
break;
case AIEvent.ET_VEHICLE_WAITING_IN_DEPOT:
INSTANCE.main.carrier.VehicleIsWaitingInDepot();
break;
case AIEvent.ET_VEHICLE_LOST:
event = AIEventVehicleLost.Convert(event);
local vehicle = event.GetVehicleID();
DInfo(cCarrier.GetVehicleName(vehicle) + " is lost, not a good news",0);
if (!AIVehicle.IsValidVehicle(vehicle)) return;
INSTANCE.main.carrier.VehicleMaintenance_Orders(vehicle);
local rcheck=INSTANCE.main.carrier.VehicleFindRouteIndex(vehicle);
INSTANCE.main.builder.RouteIsDamage(rcheck);
break;
case AIEvent.ET_VEHICLE_UNPROFITABLE:
event = AIEventVehicleUnprofitable.Convert(event);
local vehicle = event.GetVehicleID();
DInfo(cCarrier.GetVehicleName(vehicle) + " is not profitable, sending it to depot",0);
if (!AIVehicle.IsValidVehicle(vehicle)) return;
INSTANCE.main.carrier.VehicleMaintenance_Orders(vehicle);
INSTANCE.main.builder.RouteIsDamage(INSTANCE.main.carrier.VehicleFindRouteIndex(vehicle));
INSTANCE.main.carrier.VehicleSendToDepot(vehicle, DepotAction.SELL);
break;
case AIEvent.ET_COMPANY_IN_TROUBLE:
event = AIEventCompanyInTrouble.Convert(event);
local company = event.GetCompanyID();
local action="";
local info="";
local isme=AICompany.IsMine(company);
if (isme) info="My company";
else info=AICompany.GetName(company);
info+=" is in trouble. I'll take action";
DInfo(info+action,0);
if (isme)
{
local vehlist=AIVehicleList();
vehlist.Valuate(AIVehicle.GetProfitThisYear);
vehlist.KeepAboveValue(0);
foreach (vehicle, profit in vehlist)
{ // sell all non profitable vehicles
INSTANCE.main.carrier.VehicleSendToDepot(vehicle, DepotAction.SELL);
}
if (!vehlist.IsEmpty()) { INSTANCE.buildDelay = 6; }
}
break;
case AIEvent.ET_SUBSIDY_OFFER_EXPIRED:
local action = AIEventSubsidyOfferExpired.Convert(event);
cJobs.SubsidyOff(action.GetSubsidyID());
break;
case AIEvent.ET_SUBSIDY_AWARDED:
local action = AIEventSubsidyAwarded.Convert(event);
cJobs.SubsidyOff(action.GetSubsidyID());
break;
case AIEvent.ET_SUBSIDY_EXPIRED:
local action = AIEventSubsidyExpired.Convert(event);
cJobs.SubsidyOff(action.GetSubsidyID());
break;
case AIEvent.ET_SUBSIDY_OFFER:
local action = AIEventSubsidyOffer.Convert(event);
cJobs.SubsidyOn(action.GetSubsidyID());
break;
}
} // while
} // function
DictatorAI/class/croute.nut 0000644 0001750 0000144 00000031616 12203040053 015233 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cRoute extends cClass
{
static database = {};
static RouteIndexer = AIList(); // list all UID of routes we are handling
static GroupIndexer = AIList(); // map a group->UID, item=group, value=UID
static RouteDamage = AIList(); // list of routes that need repairs
static VirtualAirGroup = [-1,-1,0];// [0]=networkpassenger groupID, [1]=networkmail groupID [2]=total capacity of aircrafts in network
UID = null; // UID for that route, 0/1 for airnetwork, else = the one calc in cJobs
Name = null; // Name of the route
SourceProcess = null; // link to source process
TargetProcess = null; // link to target process
SourceStation = null; // link to source station
TargetStation = null; // link to target station
VehicleCount = null; // numbers of vehicle using it
VehicleType = null; // type of vehicle using that route (It's enum RouteType)
StationType = null; // type of station (it's AIStation.StationType)
Distance = null; // farest distance from source station to target station
Status = null; // current status of the route, see main.RouteStatus enum
GroupID = null; // groupid of the group for that route
CargoID = null; // the cargo id
DateVehicleDelete = null; // date of last time we remove a vehicle
DateHealthCheck = null; // date of last time we check route health
Source_RailEntry = null; // * if rail, do trains use that station entry=true, or exit=false
Target_RailEntry = null; // * if rail, do trains use that station entry=true, or exit=false
Primary_RailLink = null; // * true if we have build the main connecting rails from source to target station
Secondary_RailLink = null; // * true if we also build the alternate path from source to target
RailType = null; // type of rails in use, same as the first working station done
Twoway = null; // if source station and target station accept but also produce, it's a twoway route
MoreTrain = null; // if we ask one more train : 0-not yet, 1-asked, 2-accept, 3-refuse
// the state of * are store within the group name to help loading game
constructor()
{
// * are saved variables
UID = null;
Name = "UNKNOWN route";
SourceProcess = null;
TargetProcess = null;
SourceStation = null;
TargetStation = null;
VehicleCount = 0;
VehicleType = null; // *
StationType = null;
RailType = -1;
Distance = 0;
Status = 0;
GroupID = null; // *
CargoID = null;
DateVehicleDelete = 0;
DateHealthCheck = 0;
Source_RailEntry = null;
Target_RailEntry = null;
Primary_RailLink = false;
Secondary_RailLink = false;
Twoway = false;
MoreTrain = 0;
this.ClassName = "cRoute";
}
function GetRouteObject(UID)
{
if (UID in cRoute.database) { return cRoute.database[UID]; }
else { cRoute.RouteRebuildIndex(); return null; }
}
}
function cRoute::Load(uid)
// Get a route object
{
local thatroute=cRoute.GetRouteObject(uid);
if (typeof(thatroute) != "instance") { return false; }
if (thatroute instanceof cRoute) {}
else { return false; }
if (thatroute == null) { DWarn("Invalid routeID : "+uid+". Cannot get object",1); return false; }
if (thatroute.Status == RouteStatus.WORKING && thatroute.UID > 1) // in theory a working one
{
local damage = false;
if (!cMisc.ValidInstance(thatroute.SourceStation)) { damage=true; }
if (!damage && !cMisc.ValidInstance(thatroute.TargetStation)) { damage=true; }
if (!damage && !AIStation.IsValidStation(thatroute.SourceStation.s_ID)) { damage=true; }
if (!damage && !AIStation.IsValidStation(thatroute.TargetStation.s_ID)) { damage=true; }
if (damage)
{
DWarn("Route "+thatroute.Name+" is damage...",1);
if (thatroute.VehicleType == RouteType.ROAD) {
INSTANCE.main.route.RouteDamage.AddItem(uid,0);
thatroute.Status = RouteStatus.DAMAGE;
}
else { thatroute.Status = RouteStatus.DEAD; }
}
}
if (thatroute.Status != RouteStatus.WORKING) { DWarn("route "+thatroute.Name+" have a non working status : "+thatroute.Status,1); }
if (thatroute.Status == RouteStatus.DEAD) // callback the end of destruction
{
cRoute.InRemoveList(thatroute.UID);
return false;
}
return thatroute;
}
function cRoute::RouteTypeToString(that_type)
// return a string for that_type road type
{
switch (that_type)
{
case RouteType.RAIL:
return "Trains";
case RouteType.ROAD:
return "Bus & Trucks";
case RouteType.WATER:
return "Boats";
case RouteType.AIR:
case RouteType.AIRMAIL:
case RouteType.AIRNET:
case RouteType.AIRNETMAIL:
return "Big Aircrafts";
case RouteType.SMALLAIR:
case RouteType.SMALLMAIL:
return "Small Aircrafts";
case RouteType.CHOPPER:
return "Choppers";
}
return "unkown";
}
function cRoute::GetRouteName(uid)
{
local road = cRoute.Load(uid);
if (!road) { return "Invalid Route "+uid; }
return road.Name;
}
function cRoute::SetRouteName()
// set a string for that route
{
local name="### Invalid route";
local vroute = false;
local rtype=cRoute.RouteTypeToString(this.VehicleType);
if (this.UID == 0) // ignore virtual route, use by old savegame
{
name="Virtual Air Passenger Network for "+cCargo.GetCargoLabel(this.CargoID)+" using "+rtype;
vroute=true;
}
if (this.UID == 1)
{
name="Virtual Air Mail Network for "+cCargo.GetCargoLabel(this.CargoID)+" using "+rtype;
vroute=true;
}
local src=(typeof(this.SourceStation) == "instance");
local dst=(typeof(this.TargetStation) == "instance");
if (vroute) { this.Name = name; }
else
{
if (src) { src=this.SourceStation.s_Name; }
else { src=this.SourceProcess.Name; }
if (dst) { dst=this.TargetStation.s_Name; }
else { dst=this.TargetProcess.Name; }
this.Name="#"+this.UID+": From "+src+" to "+dst+" for "+cCargo.GetCargoLabel(this.CargoID)+" using "+rtype;
}
}
function cRoute::RouteSave()
// save that route to the database
{
this.SetRouteName();
if (this.UID in database) { DInfo("Route "+this.Name+" is already in database",2); }
else
{
DInfo("Adding route "+this.Name+" to the route database",2);
database[this.UID] <- this;
RouteIndexer.AddItem(this.UID, 1);
}
}
function cRoute::RouteDone()
// called when a route is finish
{
if (!cMisc.ValidInstance(this.SourceProcess) || !cMisc.ValidInstance(this.TargetProcess)) { return; }
if (!cMisc.ValidInstance(this.SourceStation) || !cMisc.ValidInstance(this.TargetStation)) { return; }
this.VehicleCount=0;
this.Status=RouteStatus.WORKING;
switch (this.VehicleType)
{
case RouteType.RAIL:
this.StationType=AIStation.STATION_TRAIN;
this.RailType = this.SourceStation.s_SubType;
break;
case RouteType.ROAD:
this.StationType=AIStation.STATION_TRUCK_STOP;
if (this.CargoID == cCargo.GetPassengerCargo()) { this.StationType=AIStation.STATION_BUS_STOP; }
break;
case RouteType.WATER:
this.StationType=AIStation.STATION_DOCK;
break;
case RouteType.AIR:
case RouteType.AIRMAIL:
case RouteType.AIRNET:
case RouteType.AIRNETMAIL:
case RouteType.SMALLAIR:
case RouteType.SMALLMAIL:
case RouteType.CHOPPER:
this.StationType=AIStation.STATION_AIRPORT;
break;
}
this.RouteSave();
this.RouteSetDistance();
if (this.SourceProcess.IsTown) { cProcess.statueTown.AddItem(this.SourceProcess.ID,0); }
if (this.TargetProcess.IsTown) { cProcess.statueTown.AddItem(this.TargetProcess.ID,0); }
this.RouteAirportCheck();
if (this.UID > 1 && (this.CargoID == cCargo.GetPassengerCargo() || this.CargoID == cCargo.GetMailCargo))
{
if (this.SourceProcess.IsTown) { cJobs.ReuseTownSet(this.SourceProcess.ID); }
if (this.TargetProcess.IsTown) { cJobs.ReuseTownSet(this.TargetProcess.ID); }
}
local srcprod=this.SourceStation.IsCargoProduce(this.CargoID);
local srcacc=this.SourceStation.IsCargoAccept(this.CargoID);
local dstprod=this.TargetStation.IsCargoProduce(this.CargoID);
local dstacc=this.TargetStation.IsCargoAccept(this.CargoID);
if (srcprod) { this.SourceStation.s_CargoProduce.AddItem(this.CargoID,0); }
if (srcacc) { this.SourceStation.s_CargoAccept.AddItem(this.CargoID,0); }
if (dstprod) { this.TargetStation.s_CargoProduce.AddItem(this.CargoID,0); }
if (dstacc) { this.TargetStation.s_CargoAccept.AddItem(this.CargoID,0); }
if (srcprod && srcacc && dstprod && dstacc) { this.Twoway=true; }
else { this.Twoway=false; }
}
function cRoute::RouteInitNetwork()
// Add the network routes to the database
{
local passRoute=cRoute();
passRoute.UID=0;
passRoute.CargoID=cCargo.GetPassengerCargo();
passRoute.VehicleType = RouteType.AIRNET;
passRoute.StationType = AIStation.STATION_AIRPORT;
passRoute.Status=RouteStatus.WORKING;
passRoute.Distance = 1000; // a dummy distance start value
local n=AIGroup.CreateGroup(AIVehicle.VT_AIR);
passRoute.GroupID=n;
cRoute.SetRouteGroupName(passRoute.GroupID, 0, 0, true, true, passRoute.CargoID, true, null, null);
cRoute.VirtualAirGroup[0]=n;
passRoute.RouteSave();
local mailRoute=cRoute();
mailRoute.UID=1;
mailRoute.CargoID=cCargo.GetMailCargo();
mailRoute.VehicleType = RouteType.AIRNETMAIL;
mailRoute.StationType = AIStation.STATION_AIRPORT;
mailRoute.Status=RouteStatus.WORKING;
mailRoute.Distance = 1000;
local n=AIGroup.CreateGroup(AIVehicle.VT_AIR);
mailRoute.GroupID=n;
cRoute.SetRouteGroupName(mailRoute.GroupID, 1, 1, true, true, mailRoute.CargoID, true, null, null);
cRoute.VirtualAirGroup[1]=n;
GroupIndexer.AddItem(cRoute.GetVirtualAirPassengerGroup(),0);
GroupIndexer.AddItem(cRoute.GetVirtualAirMailGroup(),1);
mailRoute.SourceStation = passRoute.SourceStation;
mailRoute.TargetStation = passRoute.TargetStation;
mailRoute.RouteSave();
}
function cRoute::GetVirtualAirMailGroup()
// return the groupID for the mail virtual air group
{
return cRoute.VirtualAirGroup[1];
}
function cRoute::GetVirtualAirPassengerGroup()
// return the groupID for the passenger virtual air group
{
return cRoute.VirtualAirGroup[0];
}
function cRoute::RouteSetDistance()
// Setup a route distance
{
local a, b= -1;
if (cMisc.ValidInstance(this.SourceProcess)) { a=this.SourceProcess.Location; }
if (cMisc.ValidInstance(this.TargetProcess)) { b=this.TargetProcess.Location; }
if (cMisc.ValidInstance(this.SourceStation)) { a=this.SourceStation.s_Location; }
if (cMisc.ValidInstance(this.TargetStation)) { b=this.TargetStation.s_Location; }
if (a > -1 && b > -1) { this.Distance=AITile.GetDistanceManhattanToTile(a,b); }
else { this.Distance=0; }
}
function cRoute::RouteChangeStation(uid, o_Object, n_Object)
// Route swap its old station with the new nStationObject
{
local road = cRoute.Load(uid);
if (!road) { return; }
if (road.UID < 2) { return; } // don't alter virtuals, let them reclaim it later
if (road.Status != RouteStatus.WORKING) { return; }
local vsource = cMisc.ValidInstance(road.SourceStation);
local vtarget = cMisc.ValidInstance(road.TargetStation);
local start = null;
if (vsource && o_Object.s_ID == road.SourceStation.s_ID) { start = true; }
if (vtarget && o_Object.s_ID == road.TargetStation.s_ID) { start = false; }
if (start == null) { DError("No station match in RouteChangeStation",1); return; } // no station match the old one
DInfo("Route "+uid+" is changing from station "+o_Object.s_Name+" to "+n_Object.s_Name,1);
if (start)
{
road.SourceStation.OwnerReleaseStation(uid);
road.SourceStation = n_Object;
road.SourceStation.OwnerClaimStation(uid);
}
else
{
road.TargetStation.OwnerReleaseStation(uid);
road.TargetStation = n_Object;
road.TargetStation.OwnerClaimStation(uid);
}
road.SetRouteName();
cRoute.SetRouteGroupName(road.GroupID, road.SourceProcess.ID, road.TargetProcess.ID, road.SourceProcess.IsTown, road.TargetProcess.IsTown, road.CargoID, false, road.SourceStation.s_ID, road.TargetStation.s_ID);
road.RouteAirportCheck();
}
DictatorAI/class/cbuilder.nut 0000755 0001750 0000144 00000001263 12204253332 015530 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cBuilder extends cClass
{
CriticalError=null;
building_route= null; // Keep here what main.route.we are working on
constructor()
{
building_route= -1;
this.ClassName="cBuilder";
}
}
DictatorAI/class/cstationwater.nut 0000644 0001750 0000144 00000001221 12201720507 016615 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cStationWater extends cStation
{
constructor()
{
::cStation.constructor();
this.ClassName = "cStationWater";
this.s_Type = AIStation.STATION_DOCK;
}
}
DictatorAI/handler/ 0000755 0001750 0000144 00000000000 12204523157 013517 5 ustar krinn users DictatorAI/handler/bridgehandler.nut 0000644 0001750 0000144 00000015765 12201164721 017052 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cBridge extends AIBridge
{
static bridgedatabase = {};
static BridgeList = AIList(); // list of bridge, item bridgeUID value=owner
static function GetBridgeObject(bridgeUID)
{
return bridgeUID in cBridge.bridgedatabase ? cBridge.bridgedatabase[bridgeUID] : null;
}
bridgeUID = null; // it's our internal id ( < 0)
bridgeID = null; // the bridgeID
length = null; // the length of the bridge
owner = null; // the companyID of the owner of the bridge
direction = null; // the direction the bridge is build
firstside = null; // the tile where the bridge start
otherside = null; // the tile where the bridge end
constructor()
{
bridgeUID = null;
bridgeID = null;
length = 0;
owner = AICompany.COMPANY_INVALID;
direction = AIRail.RAILTRACK_INVALID;
firstside = -1;
otherside = -1;
}
}
function cBridge::IsValidTile(tile)
// validate a bridge tile
{
if (!AIMap.IsValidTile(tile)) { return false; }
if (!AIBridge.IsBridgeTile(tile)) { return false; }
return true;
}
function cBridge::GetBridgeUID(tile)
// Return the bridge UID (our internal ID) from that tile
{
local validstart=cBridge.IsValidTile(tile);
if (!validstart) { INSTANCE.DError("This is not a bridge",2); return null; }
return 0-( (tile+1)*(AIBridge.GetOtherBridgeEnd(tile)+1) );
}
function cBridge::Save()
// Save the bridge in the database
{
this.bridgeUID=cBridge.GetBridgeUID(this.firstside);
if (bridgeUID==null) { return; } // no bridge found
this.otherside=AIBridge.GetOtherBridgeEnd(this.firstside);
if (this.bridgeUID in cBridge.bridgedatabase) { return; }
this.length=AIMap.DistanceManhattan(this.firstside, this.otherside) +1;
local dir=cBuilder.GetDirection(this.firstside, this.otherside);
if (dir == 0 || dir == 1) { this.direction = AIRail.RAILTRACK_NW_SE; }
else { this.direction = AIRail.RAILTRACK_NE_SW; }
this.owner=AICompany.ResolveCompanyID(AITile.GetOwner(this.firstside));
this.bridgeID=AIBridge.GetBridgeID(this.firstside);
cBridge.bridgedatabase[this.bridgeUID] <- this;
cBridge.BridgeList.AddItem(this.bridgeUID,this.owner);
INSTANCE.DInfo("Adding "+AIBridge.GetName(this.bridgeID)+" at "+this.firstside+" to cBridge database",2);
INSTANCE.DInfo("List of known bridges : "+(cBridge.bridgedatabase.len()),1);
}
function cBridge::Load(bUID)
// Load a bridge object, and detect if we have an UID or a tile gave
{
local cobj=cBridge();
if (AIMap.IsValidTile(bUID))
{
cobj.bridgeUID=cBridge.GetBridgeUID(bUID);
if (cobj.bridgeUID!=null) { cobj.firstside=bUID; }
else { return null; }
bUID=cobj.bridgeUID;
}
else { cobj.bridgeUID=bUID; }
if (bUID in cBridge.bridgedatabase) { cobj=cBridge.GetBridgeObject(bUID); cobj.CheckBridge(); }
else { cobj.Save(); } // we will not save a null bridgeUID
return cobj;
}
function cBridge::GetLength(bUID)
// return the length of a bridge
{
local bobj=cBridge.Load(bUID);
if (bobj == null) return 0;
return bobj.length;
}
function cBridge::CheckBridge()
// Check if the bridge need an update of its infos
{
local validstart=cBridge.IsValidTile(this.firstside);
local validend=cBridge.IsValidTile(this.otherside);
if (!validstart || !validend)
{
INSTANCE.DInfo("Bridge infos aren't valid anymore",2);
cBridge.DeleteBridge(this.bridgeUID);
}
}
function cBridge::DeleteBridge(bUID)
// delete a bridge UID (not the bridge structure itself)
{
if (bUID in cBridge.bridgedatabase)
{
delete cBridge.bridgedatabase[bUID];
BridgeList.RemoveItem(bUID);
}
}
function cBridge::GetDirection(bUID)
// return the direction the bridge is
{
local cobj=cBridge.Load(bUID);
if (cobj == null) return null;
return cobj.direction;
}
function cBridge::GetLocation(bUID)
// return the firstside (location) of the bridge
{
local cobj=cBridge.Load(bUID);
if (cobj == null) return null;
return cobj.firstside;
}
function cBridge::GetOwner(bUID)
// return the owner of the bridge
{
local cobj=cBridge.Load(bUID);
if (cobj == null) return AICompany.COMPANY_INVALID;
return cobj.owner;
}
function cBridge::GetOurBridgeList()
// return the list of bridge that we own
{
local allBridge=AIList();
allBridge.AddList(cBridge.BridgeList);
allBridge.KeepValue(AICompany.ResolveCompanyID(AICompany.COMPANY_SELF));
return allBridge;
}
function cBridge::GetBridgeID(bUID)
// return the bridgeID
{
local cobj=cBridge.Load(bUID);
if (cobj == null) return null;
return cobj.bridgeID;
}
function cBridge::GetMaxSpeed(bUID)
// return the max speed of a bridge
{
local bID=cBridge.GetBridgeID(bUID);
if (bID != null) { return AIBridge.GetMaxSpeed(bID); }
return 0;
}
function cBridge::IsBridgeTile(tile)
// return AIBridge.IsBridgeTile answer, but record the bridge if need
{
if (AIBridge.IsBridgeTile(tile))
{
local cobj=cBridge();
cobj.firstside=tile;
cobj.bridgeUID=cBridge.GetBridgeUID(tile);
cobj.Save();
return true;
}
return false;
}
function cBridge::IsRoadBridge(tile)
// return true if that bridge is a road bridge
{
local cobj=cBridge.Load(tile);
if (cobj == null) { return false; }
if (cBridge.IsBridgeTile(cobj.firstside) && AITile.HasTransportType(cobj.firstside, AITile.TRANSPORT_ROAD)) { return true; }
return false;
}
function cBridge::IsRailBridge(bUID)
// return true if that bridge is a rail bridge
{
local cobj=cBridge.Load(bUID);
if (cobj == null) { return false; }
if (cBridge.IsBridgeTile(cobj.firstside) && AITile.HasTransportType(cobj.firstside, AITile.TRANSPORT_RAIL)) { return true; }
return false;
}
function cBridge::GetCheapBridgeID(btype, length, needresult=true)
// return a bridge ID to build a bridge of that size and type at needed speed
{
local needSpeed=cEngineLib.GetTrainMaximumSpeed();//INSTANCE.main.carrier.speed_MaxTrain;
if (btype == AIVehicle.VT_ROAD) { needSpeed=INSTANCE.main.carrier.speed_MaxRoad; }
if (needSpeed == 0)
{
local vehlist=AIEngineList(btype);
vehlist.Valuate(AIEngine.GetMaxSpeed);
vehlist.KeepAboveValue(1); // remove 0 speed engines
vehlist.Sort(AIList.SORT_BY_VALUE, true);
if (!vehlist.IsEmpty()) { needSpeed=vehlist.GetValue(vehlist.Begin()); }
}
local blist=AIBridgeList_Length(length);
blist.Valuate(AIBridge.GetMaxSpeed);
blist.KeepAboveValue((needSpeed -1));
blist.Sort(AIList.SORT_BY_VALUE, true); // slowest first as this are all bridges faster than our train speed anyway
if (blist.IsEmpty() && needresult) { blist=AIBridgeList_Length(length); blist.Sort(AIList.SORT_BY_VALUE, false); } // faster first
if (blist.IsEmpty()) { return -1; }
else { return blist.Begin(); }
}
DictatorAI/handler/ordershandler.nut 0000644 0001750 0000144 00000044473 12204523154 017114 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cCarrier::VehicleOrderSkipCurrent(veh)
// Skip the current order and go to the next one
{
local current=AIOrder.ResolveOrderPosition(veh, AIOrder.ORDER_CURRENT);
local total=AIOrder.GetOrderCount(veh);
if (current+1 == total) { current=0; }
else { current++; }
AIOrder.SkipToOrder(veh, current);
}
function cCarrier::AirNetworkOrdersHandler()
// Create orders for aircrafts that run the air network
{
local road=null;
local isfirst=true;
local mailrabbit=null; // this will be our rabbit aircraft that take orders & everyone share them with it
local passrabbit=null;
local mailgroup=AIVehicleList_Group(cRoute.GetVirtualAirMailGroup());
local passgroup=AIVehicleList_Group(cRoute.GetVirtualAirPassengerGroup());
mailgroup.RemoveList(cCarrier.ToDepotList);
passgroup.RemoveList(cCarrier.ToDepotList);
mailgroup.Valuate(AIVehicle.GetState);
passgroup.Valuate(AIVehicle.GetState);
mailgroup.RemoveValue(AIVehicle.VS_CRASHED);
mailgroup.RemoveValue(AIVehicle.VS_IN_DEPOT);
passgroup.RemoveValue(AIVehicle.VS_CRASHED);
passgroup.RemoveValue(AIVehicle.VS_IN_DEPOT);
passrabbit = passgroup.Begin();
mailrabbit = mailgroup.Begin();
local numorders = null;
if (!passgroup.IsEmpty())
{
passrabbit = passgroup.Begin();
local temp = AIList(); temp.AddList(passgroup);
foreach (vehicle, _ in temp)
{
local dest = AIOrder.GetOrderDestination(vehicle, AIOrder.ORDER_CURRENT);
if (!AIOrder.IsCurrentOrderPartOfOrderList(vehicle)) { dest = AIStation.GetLocation(AIStation.GetStationID(dest)); }
// When servicing, it should be done at destination airport hangar, but won't be in the order list, so find its real destination
passgroup.SetValue(vehicle, dest);
if (vehicle == passrabbit) { continue; }
AIOrder.ShareOrders(vehicle, passrabbit);
}
numorders = AIOrder.GetOrderCount(passrabbit);
if (numorders != cCarrier.VirtualAirRoute.len())
{
AIOrder.UnshareOrders(passrabbit);
cCarrier.VehicleOrdersReset(passrabbit);
numorders = 0;
for (local i=0; i < cCarrier.VirtualAirRoute.len(); i++)
{
local destination = cCarrier.VirtualAirRoute[i];
if (AIOrder.AppendOrder(passrabbit, destination, AIOrder.OF_NONE))
{ numorders++; }
else
{
DError("Passenger rabbit refuse order, destination: "+destination,2);
cCarrier.VirtualAirRoute.remove(i);
}
}
foreach (vehicle, destination in passgroup)
{
// now try to get it back to its initial station destination
local wasorder = VehicleFindDestinationInOrders(vehicle, AIStation.GetStationID(destination));
if (wasorder != -1) { AIOrder.SkipToOrder(vehicle, wasorder); }
else { AIOrder.SkipToOrder(vehicle, AIBase.RandRange(numorders)); }
}
}
}
if (!mailgroup.IsEmpty())
{
local temp = AIList();
temp.AddList(mailgroup);
foreach (vehicle, _ in temp)
{
local dest = AIOrder.GetOrderDestination(vehicle, AIOrder.ORDER_CURRENT);
if (!AIOrder.IsCurrentOrderPartOfOrderList(vehicle)) { dest = AIStation.GetLocation(AIStation.GetStationID(dest)); }
mailgroup.SetValue(vehicle, dest);
if (vehicle == mailrabbit) { continue; }
AIOrder.ShareOrders(vehicle, mailrabbit);
}
// save current order for each vehicle
numorders = AIOrder.GetOrderCount(mailrabbit);
if (numorders != cCarrier.VirtualAirRoute.len())
{
AIOrder.UnshareOrders(mailrabbit);
cCarrier.VehicleOrdersReset(mailrabbit);
numorders = 0;
for (local i=0; i < cCarrier.VirtualAirRoute.len(); i++)
{
local destination = cCarrier.VirtualAirRoute[((cCarrier.VirtualAirRoute.len()-1)-i)];
if (AIOrder.AppendOrder(mailrabbit, destination, AIOrder.OF_NONE))
{ numorders++; }
else
{
DError("Mail rabbit refuse order, destination: "+destination,2);
cDebug.PutSign(destination, "REFUSE_ORDER");
}
}
foreach (vehicle, destination in mailgroup)
{
// now try to get it back to its initial station destination
local wasorder=VehicleFindDestinationInOrders(vehicle, AIStation.GetStationID(destination));
if (wasorder != -1) { AIOrder.SkipToOrder(vehicle, wasorder); }
else { AIOrder.SkipToOrder(vehicle, AIBase.RandRange(numorders)); }
}
}
}
}
function cCarrier::VehicleOrdersReset(veh)
// Remove all orders for veh
{
while (AIOrder.GetOrderCount(veh) > 0)
{
if (!AIOrder.RemoveOrder(veh, AIOrder.ResolveOrderPosition(veh, 0)))
{ DError("Cannot remove orders ",2); break; }
}
}
function cCarrier::VehicleBuildOrders(groupID, orderReset)
// Redo all orders vehicles from that group should have
// orderReset true to reset all vehicles orders in the group, false to simply reassign sharing orders to vehicle in the group
{
if (groupID == null || !AIGroup.IsValidGroup(groupID)) { return false; }
local vehlist=AIVehicleList_Group(groupID);
if (vehlist.IsEmpty()) { return false; }
vehlist.Valuate(AIVehicle.GetState);
vehlist.RemoveValue(AIVehicle.VS_STOPPED);
vehlist.RemoveValue(AIVehicle.VS_IN_DEPOT);
vehlist.RemoveValue(AIVehicle.VS_CRASHED);
vehlist.RemoveList(cCarrier.ToDepotList);
if (vehlist.IsEmpty()) { return true; }
local veh = vehlist.Begin();
local filterveh=AIList();
filterveh.AddList(vehlist);
filterveh.Valuate(AIOrder.GetOrderCount);
filterveh.KeepValue(2); // only a 2 orders vehicle is valid for us
if (filterveh.IsEmpty()) { orderReset=true; } // no vehicle with valid orders is usable as sharing target
else { veh=filterveh.Begin(); }
local idx=INSTANCE.main.carrier.VehicleFindRouteIndex(veh);
local road = cRoute.Load(idx);
if (!road) { return false; }
if (!typeof(road.SourceStation) == "instance") { return false; }
if (!typeof(road.TargetStation) == "instance") { return false; }
local oneorder=null;
local twoorder=null;
local srcplace=null;
local dstplace=null;
local noshare = false;
switch (road.VehicleType)
{
case AIVehicle.VT_ROAD:
oneorder = AIOrder.OF_NON_STOP_INTERMEDIATE;
twoorder = AIOrder.OF_NON_STOP_INTERMEDIATE;
if (!road.Twoway) { oneorder+=AIOrder.OF_FULL_LOAD_ANY; twoorder+=AIOrder.OF_NO_LOAD; }
srcplace= road.SourceStation.s_Location;
dstplace= road.TargetStation.s_Location;
break;
case AIVehicle.VT_RAIL:
oneorder = AIOrder.OF_NON_STOP_INTERMEDIATE;
twoorder = AIOrder.OF_NON_STOP_INTERMEDIATE;
if (!road.Twoway) { oneorder+=AIOrder.OF_FULL_LOAD_ANY; twoorder+=AIOrder.OF_NO_LOAD; }
srcplace= road.SourceStation.s_Location;
dstplace= road.TargetStation.s_Location;
noshare = true;
break;
case RouteType.AIR:
case RouteType.AIRMAIL:
case RouteType.SMALLAIR:
case RouteType.SMALLMAIL:
oneorder=AIOrder.OF_NONE;
twoorder=AIOrder.OF_NONE;
srcplace= road.SourceStation.s_Location;
dstplace= road.TargetStation.s_Location;
break;
case AIVehicle.VT_WATER:
oneorder = AIOrder.OF_NON_STOP_INTERMEDIATE;
twoorder = AIOrder.OF_NON_STOP_INTERMEDIATE;
if (!road.Twoway) { oneorder+=AIOrder.OF_FULL_LOAD_ANY; twoorder+=AIOrder.OF_NO_LOAD; }
srcplace= road.SourceStation.s_Location;
dstplace= road.TargetStation.s_Location;
break;
case RouteType.AIRNET:
case RouteType.AIRNETMAIL: // it's the air network
INSTANCE.main.carrier.AirNetworkOrdersHandler();
return true;
break;
case RouteType.CHOPPER:
oneorder=AIOrder.OF_NONE;
twoorder=AIOrder.OF_NONE;
srcplace= road.SourceStation.s_Location;
dstplace= road.TargetStation.s_Location;
break;
}
if (srcplace == -1 || dstplace == -1) { return false; }
DInfo("Setting orders for route "+cRoute.GetRouteName(idx),2);
if (orderReset)
{
INSTANCE.main.carrier.VehicleOrdersReset(veh);
if (!AIOrder.AppendOrder(veh, srcplace, oneorder))
{ DError("First order refuse",2); }
if (!AIOrder.AppendOrder(veh, dstplace, twoorder))
{ DError("Second order refuse",2); }
}
vehlist.RemoveItem(veh);
if (!noshare) { foreach (vehicle, dummy in vehlist) AIOrder.ShareOrders(vehicle, veh); }
return true;
}
function cCarrier::VehicleFindDestinationInOrders(vehicle, stationID)
// browse vehicle orders and return index of order that target that stationID
{
local numorders=AIOrder.GetOrderCount(vehicle);
if (numorders==0) { return -1; }
for (local j=0; j < numorders; j++)
{
local tiletarget=AIOrder.GetOrderDestination(vehicle,AIOrder.ResolveOrderPosition(vehicle, j));
if (!AITile.IsStationTile(tiletarget)) { continue; }
local targetID=AIStation.GetStationID(tiletarget);
if (targetID == stationID) { return j; }
}
return -1;
}
function cCarrier::FindClosestHangarForAircraft(veh)
// return closest airport where we could send an aircraft
{
if (AIVehicle.GetVehicleType(veh) != AIVehicle.VT_AIR) { return -1; } // only for aircraft
local vehloc=AIVehicle.GetLocation(veh);
local temp = AIStationList(AIStation.STATION_AIRPORT);
local airports = AIList();
temp.Valuate(AIStation.GetLocation);
foreach (staID, locations in temp) if (AIAirport.GetNumHangars(locations) != 0) { airports.AddItem(staID,0); }
// remove station without hangars
if (!airports.IsEmpty())
{
airports.Valuate(AIStation.GetDistanceManhattanToTile, vehloc);
airports.Sort(AIList.SORT_BY_VALUE, true); // closest one
return AIAirport.GetHangarOfAirport(AIStation.GetLocation(airports.Begin()));
}
return -1;
}
function cCarrier::TrainSetDepotOrder(veh)
// Set orders to force a train going to depot
{
if (veh == null) { return; }
local idx=INSTANCE.main.carrier.VehicleFindRouteIndex(veh);
local road=cRoute.Load(idx);
if (!road)
{
DError("Gonna be a hard time, i don't know who own that train "+cCarrier.GetVehicleName(veh),1);
if (AIVehicle.SendVehicleToDepot(veh)) { cCarrier.ToDepotList.AddItem(veh, DepotAction.SELL); }
return false;
}
local srcDepot = cRoute.GetDepot(idx, 1);
local dstDepot = cRoute.GetDepot(idx, 2);
if (!AIRail.IsRailDepotTile(srcDepot)) { srcDepot = dstDepot; }
if (!AIRail.IsRailDepotTile(dstDepot)) { dstDepot = srcDepot; }
if (!AIRail.IsRailDepotTile(srcDepot)) { DError("Cannot send train to a depot as i cannot find any valid depot where sent it.",1); return false; }
if (AIOrder.GetOrderCount(veh) != 2)
{
DWarn("Train "+cCarrier.GetVehicleName(veh)+" doesn't have valid number of orders.",1);
cCarrier.VehicleOrdersReset(veh);
cCarrier.TrainSetOrders(veh);
}
AIOrder.SetOrderFlags(veh, 0, AIOrder.OF_NON_STOP_INTERMEDIATE); // avoid cargodist bug
if (!AIOrder.InsertOrder(veh, 1, srcDepot, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto closest airport order",2); }
if (!AIOrder.InsertOrder(veh, 3, dstDepot, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto closest airport order",2); }
}
function cCarrier::VehicleSetDepotOrder(veh)
// set all orders of the vehicle to force it going to a depot
{
if (veh == null) { return; }
local idx=INSTANCE.main.carrier.VehicleFindRouteIndex(veh);
local road=cRoute.Load(idx);
local homedepot = null;
local srcValid = false;
local dstValid = false;
local isAircraft = (AIVehicle.GetVehicleType(veh) == AIVehicle.VT_AIR);
if (road != false)
{
homedepot=road.GetDepot(idx);
srcValid = (typeof(road.SourceStation) == "instance");
dstValid = (typeof(road.TargetStation) == "instance");
}
local prevDest=AIOrder.GetOrderDestination(veh, AIOrder.ORDER_CURRENT);
AIOrder.UnshareOrders(veh);
INSTANCE.main.carrier.VehicleOrdersReset(veh);
if (homedepot == null || !cStation.IsDepot(homedepot))
{
local vehloc=AIVehicle.GetLocation(veh);
if (AIVehicle.GetVehicleType(veh)==AIVehicle.VT_ROAD)
{
cCarrier.StopVehicle(veh);
// first stop it from running everywhere
vehloc=AIVehicle.GetLocation(veh); // now that the vehicle is stopped
local possibleplace=cTileTools.GetTilesAroundPlace(AIVehicle.GetLocation(veh),100);
local depottile=AIList();
depottile.AddList(possibleplace);
depottile.Valuate(AIRoad.IsRoadDepotTile);
depottile.KeepValue(1);
depottile.Valuate(AITile.GetOwner);
depottile.KeepValue(AICompany.ResolveCompanyID(AICompany.COMPANY_SELF));
if (!depottile.IsEmpty())
{
foreach (depotloc, dummy in depottile)
if (INSTANCE.main.builder.RoadRunner(vehloc, depotloc, AIVehicle.VT_ROAD))
{ homedepot=depotloc; DInfo("Sending "+cCarrier.GetVehicleName(veh)+" to a backup depot we found near it",1); break; }
}
else
{
DInfo("Trying to build a depot to sent "+cCarrier.GetVehicleName(veh)+" there",1);
homedepot=cBuilder.BuildRoadDepotAtTile(vehloc, -1);
if (homedepot==-1) { homedepot==null; }
}
if (homedepot == null) { return; }
cCarrier.StartVehicle(veh);
}
}
if (srcValid && !isAircraft) { AIOrder.AppendOrder(veh, road.SourceStation.s_Location, AIOrder.OF_NONE); }
local orderindex = 0;
if (isAircraft)
{
local shortpath = FindClosestHangarForAircraft(veh);
DInfo("Routing aircraft "+cCarrier.GetVehicleName(veh)+" to the closest airport at "+shortpath,2);
if (!AIOrder.AppendOrder(veh, shortpath, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto closest airport order",2); }
}
if (homedepot != null)
{
if (!AIOrder.AppendOrder(veh, homedepot, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto source depot order",2); }
if (!AIOrder.AppendOrder(veh, homedepot, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto source depot order",2); }
if (!AIOrder.AppendOrder(veh, homedepot, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto source depot order",2); }
}
// Adding depot orders 3 time, so we should endup with at least 3 orders minimum to avoid get caught again by orders check
if (dstValid && cStation.IsDepot(road.TargetStation.s_Depot)) { homedepot=road.TargetStation.s_Depot; }
if (dstValid && !isAircraft) { AIOrder.AppendOrder(veh, road.TargetStation.s_Location, AIOrder.OF_NONE); }
if (homedepot != null)
{
if (!AIOrder.AppendOrder(veh, homedepot, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto destination depot order",2); }
if (!AIOrder.AppendOrder(veh, homedepot, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto destination depot order",2); }
if (!AIOrder.AppendOrder(veh, homedepot, AIOrder.OF_STOP_IN_DEPOT))
{ DError("Vehicle refuse goto destination depot order",2); }
}
if (road != false && !isAircraft)
for (local jjj=0; jjj < AIOrder.GetOrderCount(veh); jjj++)
// this send vehicle to met dropoff station before its depot
{
if (!dstValid) { break; }
if (AIVehicle.GetVehicleType(veh) == AIVehicle.VT_RAIL)
{
if (AIOrder.GetOrderDestination(veh, AIOrder.ORDER_CURRENT) != prevDest)
{
AIOrder.SkipToOrder(veh, jjj+1);
}
else { AIOrder.SkipToOrder(veh, jjj+1); break; }
}
else if (AIOrder.GetOrderDestination(veh, AIOrder.ORDER_CURRENT) != road.TargetStation.s_Location)
{
AIOrder.SkipToOrder(veh, jjj+1);
DInfo("Sending vehicle "+cCarrier.GetVehicleName(veh)+" to destination station",1);
break;
}
}
if (isAircraft)
{
// try to force it going at order 0 instead of current destination. openttd orders are weak
AIOrder.SkipToOrder(veh, 1);
AIOrder.SkipToOrder(veh, 0);
}
DInfo("Setting depot order for vehicle "+INSTANCE.main.carrier.GetVehicleName(veh),2);
}
function cCarrier::VehicleOrderIsValid(vehicle,orderpos)
// Really check if a vehicle order is valid
{
local ordercount=AIOrder.GetOrderCount(vehicle);
if (ordercount == 0) { return true; }
local ordercheck = AIOrder.ResolveOrderPosition(vehicle, orderpos);
if (!AIOrder.IsValidVehicleOrder(vehicle, ordercheck)) { print("caught bad order from AIOrder.IsValidOrder"); return false; }
local tiletarget=AIOrder.GetOrderDestination(vehicle, ordercheck);
local vehicleType=AIVehicle.GetVehicleType(vehicle);
local stationID=AIStation.GetStationID(tiletarget);
switch (vehicleType)
{
case AIVehicle.VT_RAIL:
local is_station=AIStation.HasStationType(stationID,AIStation.STATION_TRAIN);
local is_depot=AIRail.IsRailDepotTile(tiletarget);
if (!is_depot && !is_station) { return false; }
break;
case AIVehicle.VT_WATER:
local is_station=AIStation.HasStationType(stationID,AIStation.STATION_DOCK);
local is_depot=AIMarine.IsWaterDepotTile(tiletarget);
if (!is_station && !is_depot) { print("caught by VT_WATER check"); return false; }
break;
case AIVehicle.VT_AIR:
local is_station=AIStation.HasStationType(stationID,AIStation.STATION_AIRPORT);
local is_depot=AIAirport.GetHangarOfAirport(tiletarget);
if (!is_station && !is_depot) { return false; }
break;
case AIVehicle.VT_ROAD:
local truckcheck=AIStation.HasStationType(stationID,AIStation.STATION_TRUCK_STOP);
local buscheck=AIStation.HasStationType(stationID,AIStation.STATION_BUS_STOP);
local depotcheck=AIRoad.IsRoadDepotTile(tiletarget);
if (!truckcheck && !buscheck && !depotcheck) { return false; }
break;
}
return true;
}
function cCarrier::TrainSetOrders(trainID)
// Set orders for a train
{
local uid=INSTANCE.main.carrier.VehicleFindRouteIndex(trainID);
if (uid==null) { DError("Cannot find route uid for that train",1); return false; }
local road=cRoute.GetRouteObject(uid);
if (!road) { return false; }
//if (road.status ==
cCarrier.VehicleOrdersReset(trainID);
DInfo("Append orders to "+cCarrier.GetVehicleName(trainID),2);
local firstorder=AIOrder.OF_NON_STOP_INTERMEDIATE;
local secondorder=AIOrder.OF_NON_STOP_INTERMEDIATE;
if (!road.Twoway) { firstorder+=AIOrder.OF_FULL_LOAD_ANY; secondorder=AIOrder.OF_NO_LOAD; }
if (!AIOrder.AppendOrder(trainID, AIStation.GetLocation(road.SourceStation.s_ID), firstorder))
{ DError(cCarrier.GetVehicleName(trainID)+" refuse first order",2); return false; }
if (!AIOrder.AppendOrder(trainID, AIStation.GetLocation(road.TargetStation.s_ID), secondorder))
{ DError(cCarrier.GetVehicleName(trainID)+" refuse second order",2); return false; }
return true;
}
DictatorAI/handler/chemin.nut 0000755 0001750 0000144 00000040122 12203142645 015512 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cRoute::RouteUpdateAirPath()
// update the infos for our specials routes for the air network
{
if (cCarrier.VirtualAirRoute.len() < 2) { return; }
local oneAirportID=AIStation.GetStationID(cCarrier.VirtualAirRoute[0]);
local twoAirportID=AIStation.GetStationID(cCarrier.VirtualAirRoute[1]);
local network = cRoute.Load(0);
network.SourceStation = cStation.Load(oneAirportID);
network.TargetStation = cStation.Load(twoAirportID);
if (cMisc.ValidInstance(network.SourceStation) && cMisc.ValidInstance(network.TargetStation)) { network.Status = RouteStatus.WORKING; }
else { network.Status = RouteStatus.DAMAGE; }
network.SourceProcess = cProcess.Load(cProcess.GetUID(cStation.VirtualAirports.GetValue(oneAirportID), true));
network.TargetProcess = cProcess.Load(cProcess.GetUID(cStation.VirtualAirports.GetValue(twoAirportID), true));
local mailnet = cRoute.Load(1);
mailnet.SourceStation = network.SourceStation;
mailnet.TargetStation = network.TargetStation;
mailnet.SourceProcess = network.SourceProcess;
mailnet.TargetProcess = network.TargetProcess;
mailnet.Status = network.Status;
}
function cRoute::VirtualAirNetworkUpdate()
// update our list of airports that are in the air network
{
local virtroad=cRoute.Load(0); // 0 is always the passenger one
if (!virtroad) { return; }
virtroad.Distance=0;
local towns=AITownList();
towns.Valuate(AITown.GetPopulation);
towns.RemoveBelowValue(INSTANCE.main.carrier.AIR_NET_CONNECTOR);
DInfo("NETWORK: Found "+towns.Count()+" towns for network",1);
if (towns.Count() < 3) { return; } // give up
local airports = AIStationList(AIStation.STATION_AIRPORT);
local ztemp = AIStationList(AIStation.STATION_AIRPORT);
ztemp.Valuate(AIStation.GetLocation);
foreach (airID, location in ztemp)
{
local dummy = cLooper();
airports.SetValue(airID,1);
if (AIAirport.GetAirportType(location) == AIAirport.AT_SMALL) { airports.SetValue(airID, 0); }
if (AIAirport.GetNumHangars(location) == 0) { airports.SetValue(airID, 0); }
local not_own = cStation.Load(airID);
if (!not_own || not_own.s_Owner.IsEmpty()) { airports.SetValue(airID, 0); }
}
airports.RemoveValue(0); // don't network small airports, not yet own airport & platform, it's too hard for slow aircrafts
if (airports.IsEmpty()) { return; }
else { DInfo("NETWORK: Found "+airports.Count()+" valid airports for network",1); }
airports.Valuate(AIStation.GetLocation);
local virtualpath=AIList();
local validairports=AIList();
foreach (airport_id, location in airports)
{
local check=AIAirport.GetNearestTown(location, AIAirport.GetAirportType(location));
if (towns.HasItem(check))
{
validairports.AddItem(check, airport_id);
virtualpath.AddItem(check, towns.GetValue(check));
}
}
if (virtualpath.Count() < 3) { return; } // we cannot work with less than 2 airports
virtualpath.Sort(AIList.SORT_BY_VALUE, false);
// now validairports = only airports where towns population is > AIR_NET_CONNECTOR, value is airportid
// and virtualpath the town where those airports are, value = population of those towns
local bigtown=virtualpath.Begin();
local bigtown_location=AITown.GetLocation(bigtown);
virtualpath.Valuate(AITown.GetDistanceManhattanToTile, bigtown_location);
virtualpath.Sort(AIList.SORT_BY_VALUE,true);
local impair=false;
local pairlist=AIList();
local impairlist=AIList();
foreach (towns, distances in virtualpath)
{
if (impair) { impairlist.AddItem(towns, distances); }
else { pairlist.AddItem(towns, distances); }
impair=!impair;
}
pairlist.Sort(AIList.SORT_BY_VALUE,true);
impairlist.Sort(AIList.SORT_BY_VALUE,false);
virtualpath.Clear();
INSTANCE.main.carrier.VirtualAirRoute.clear(); // don't try reassign a static variable!
foreach (towns, dummy in pairlist) INSTANCE.main.carrier.VirtualAirRoute.push(AIStation.GetLocation(validairports.GetValue(towns)));
foreach (towns, dummy in impairlist) INSTANCE.main.carrier.VirtualAirRoute.push(AIStation.GetLocation(validairports.GetValue(towns)));
local lastdistance=AITile.GetDistanceManhattanToTile(INSTANCE.main.carrier.VirtualAirRoute[0], INSTANCE.main.carrier.VirtualAirRoute[INSTANCE.main.carrier.VirtualAirRoute.len()-1]);
for (local i=0; i < INSTANCE.main.carrier.VirtualAirRoute.len(); i++)
{
local step=0;
if (i == 0) { step = lastdistance; }
else { step = AITile.GetDistanceManhattanToTile(INSTANCE.main.carrier.VirtualAirRoute[i], INSTANCE.main.carrier.VirtualAirRoute[i-1]); }
if (virtroad.Distance < step) { virtroad.Distance=step; } // setup newGRF distance limit
}
local vehlist=AIList();
local maillist=AIVehicleList_Group(INSTANCE.main.route.GetVirtualAirMailGroup());
local passlist=AIVehicleList_Group(INSTANCE.main.route.GetVirtualAirPassengerGroup());
vehlist.AddList(maillist);
vehlist.AddList(passlist);
local vehnumber=vehlist.Count();
/*
if (INSTANCE.main.carrier.VirtualAirRoute.len() > 1)
foreach (towns, airportid in validairports)
{
local zzz = cLooper();
if (!cStation.VirtualAirports.HasItem(airportid))
{
cStation.VirtualAirports.AddItem(airportid, towns);
local stealgroup = AIList();
local steal_temp = AIVehicleList_Station(airportid);
steal_temp.Valuate(AIVehicle.GetEngineType);
foreach (veh, vehtype in steal_temp)
{
if (AIEngine.GetPlaneType(vehtype) == AIAirport.PT_HELICOPTER) { continue; } // don't steal choppers
stealgroup.AddItem(veh, AIVehicle.GetGroupID(veh));
}
stealgroup.RemoveValue(cRoute.GetVirtualAirPassengerGroup());
stealgroup.RemoveValue(cRoute.GetVirtualAirMailGroup());
stealgroup.RemoveTop(2);
if (stealgroup.IsEmpty()) { continue; }
DInfo("Reassigning "+stealgroup.Count()+" aircrafts to the network",0);
local thatnetwork=0;
foreach (vehicle, gid in stealgroup)
{
if (cCarrier.ToDepotList.HasItem(vehicle)) { continue; }
if (vehnumber % 6 == 0) { thatnetwork=cRoute.GetVirtualAirMailGroup(); }
else { thatnetwork=cRoute.GetVirtualAirPassengerGroup(); }
AIGroup.MoveVehicle(thatnetwork, vehicle);
cCarrier.VehicleOrdersReset(vehicle); // reset order, force order change
vehnumber++;
}
}
}
*/
DInfo("NETWORK -> Airnetwork route length is now : "+cCarrier.VirtualAirRoute.len()+" max distance="+virtroad.Distance,1);
INSTANCE.main.route.RouteUpdateAirPath();
INSTANCE.main.carrier.AirNetworkOrdersHandler();
}
function cRoute::DutyOnAirNetwork()
// handle the traffic for the aircraft network
{
if (INSTANCE.main.carrier.VirtualAirRoute.len()<2) { return; }
local virtroad=cRoute.Load(0);
if (!virtroad) { return; }
if (virtroad.Status != 100) { return; }
local vehlist=AIList();
local passengerID=cCargo.GetPassengerCargo();
local maillist=AIVehicleList_Group(INSTANCE.main.route.GetVirtualAirMailGroup());
local passlist=AIVehicleList_Group(INSTANCE.main.route.GetVirtualAirPassengerGroup());
vehlist.AddList(passlist);
local totalcapacity=0;
local onecapacity=0;
local age=0;
local vehneed=0;
local vehnumber=maillist.Count()+passlist.Count();
DInfo("NETWORK: Aircrafts in network: "+vehnumber+" max dist: "+virtroad.Distance,1);
local futurveh= cCarrier.GetAirVehicle(null, cCargo.GetMailCargo(), AircraftType.BEST); // force discovery of new engine for the virtual mail network
futurveh= cCarrier.GetAirVehicle(null, passengerID, AircraftType.BEST);
if (futurveh == -1) { return; } // when aircrafts are disable, return -1
if (vehlist.IsEmpty())
{
onecapacity=AIEngine.GetCapacity(futurveh);
age=1000;
vehneed=1;
}
else
{
vehlist.Valuate(AIVehicle.GetCapacity,cCargo.GetPassengerCargo());
onecapacity=0;
foreach (vehicle, capacity in vehlist)
{
totalcapacity+=capacity;
if (capacity > 0) { onecapacity=capacity; }
}
cRoute.VirtualAirGroup[2]=totalcapacity;
vehlist.Valuate(AIVehicle.GetAge);
vehlist.Sort(AIList.SORT_BY_VALUE,true); // younger first
age=vehlist.GetValue(vehlist.Begin());
if (age < 60) { DInfo("We already buy an aircraft recently for the network: "+age,2); return; }
}
if (onecapacity == 0) { onecapacity=90; } // estimation
DInfo("NETWORK: Total capacity of network: "+totalcapacity,1);
local bigairportlocation = INSTANCE.main.carrier.VirtualAirRoute[0];
local bigairportID = AIStation.GetStationID(bigairportlocation);
local bigairportObj = cStation.Load(bigairportID);
if (!bigairportObj) { return; }
bigairportObj.UpdateStationInfos();
/*
local cargowaiting=bigairportObj.s_CargoProduce.GetValue(passengerID);
if ((cargowaiting-totalcapacity) > 0) { vehneed = cargowaiting / onecapacity; }
local overcharge=AITown.GetLastMonthProduction(AITile.GetClosestTown(bigairportlocation), passengerID) / 2;
if (vehneed==0 && AIStation.GetCargoRating(bigairportID, passengerID) < 45 && totalcapacity < overcharge)
{
vehneed=1;
DInfo("NETWORK: overcharging network capacity to increase rating",1);
}
// one because poor station rating
if (vehnumber < (cCarrier.VirtualAirRoute.len() * 2)) { vehneed=(cCarrier.VirtualAirRoute.len() *2) - vehnumber; }
DInfo("NETWORK: need="+vehneed,1);
cDebug.PutSign(bigairportlocation,"Network Airport Reference: "+cargowaiting);
if (vehneed > 6) { vehneed=6; } // limit to 6 aircrafts add per trys
*/
vehneed = (cCarrier.VirtualAirRoute.len() * INSTANCE.main.carrier.airnet_max) - vehnumber;
local tomail = 0;
local topass = 0;
if (vehneed > 0)
{
for (local k = 0; k < vehneed; k++)
{
if (INSTANCE.main.carrier.CanAddNewVehicle(0, true, k))
{
if ((vehnumber + k) % 3 == 0) { tomail++; }
else { topass++; }
}
else { break; }
}
cCarrier.RouteNeedVehicle(cRoute.GetVirtualAirMailGroup(), tomail);
cCarrier.RouteNeedVehicle(cRoute.GetVirtualAirPassengerGroup(), topass);
}
}
function cRoute::VehicleGroupProfitRatio(groupID)
// check a vehicle group and return a ratio representing it's value
// it's just (groupprofit * 1000 / numbervehicle)
{
if (!AIGroup.IsValidGroup(groupID)) { return 0; }
local vehlist=AIVehicleList_Group(groupID);
local vehnumber=vehlist.Count();
if (vehnumber == 0) { return 1000000; } // avoid / per 0 and set high value to group without vehicle
local totalvalue=0;
vehlist.Valuate(AIVehicle.GetProfitThisYear);
foreach (vehicle, value in vehlist)
{ totalvalue+=value*100; }
return totalvalue / vehnumber;
}
function cRoute::DutyOnRoute()
// this is where we add vehicle and tiny other things to max our money
{
DInfo("Managing stations",0);
if (!INSTANCE.main.carrier.vehicle_wishlist.IsEmpty())
{
cCarrier.Process_VehicleWish();
}
local firstveh=false;
local priority=AIList();
local road=null;
local chopper=false;
local dual=false;
INSTANCE.main.route.DutyOnAirNetwork(); // we handle the network load here
foreach (uid, dummy in cRoute.RouteIndexer)
{
local pause = cLooper();
firstveh=false;
road = cRoute.Load(uid);
if (!road) { continue; }
if (road.Status != RouteStatus.WORKING) { continue; }
if (road.VehicleType == RouteType.AIRNET || road.VehicleType == RouteType.AIRNETMAIL) { continue; }
if (road.VehicleType == RouteType.RAIL) { INSTANCE.main.route.DutyOnRailsRoute(uid); continue; }
local maxveh=0;
local cargoid=road.CargoID;
local futur_engine=INSTANCE.main.carrier.GetVehicle(uid);
local futur_engine_capacity=1;
if (futur_engine != null) { futur_engine_capacity=cEngine.GetCapacity(futur_engine, road.CargoID); }
else { continue; }
switch (road.VehicleType)
{
case RouteType.ROAD:
if (!INSTANCE.use_road) { continue; }
maxveh=INSTANCE.main.carrier.road_max_onroute;
break;
case RouteType.CHOPPER:
if (!INSTANCE.use_air) { continue; }
chopper=true;
maxveh=4;
cargoid=cCargo.GetPassengerCargo();
INSTANCE.main.builder.DumpRoute(uid);
break;
case RouteType.AIR:
case RouteType.AIRMAIL:
case RouteType.SMALLAIR:
case RouteType.SMALLMAIL:
if (!INSTANCE.use_air) { continue; }
maxveh=INSTANCE.main.carrier.air_max;
cargoid=cCargo.GetPassengerCargo(); // for aircraft, force a check vs passenger
// so mail aircraft runner will be add if passenger is high enough, this only affect routes not in the network
break;
case AIVehicle.VT_WATER:
if (!INSTANCE.use_boat) { continue; }
maxveh=INSTANCE.main.carrier.water_max;
break;
}
road.SourceStation.UpdateStationInfos();
DInfo("Route "+road.Name+" distance "+road.Distance,2);
local vehneed=0;
local vehonroute=road.VehicleCount;
if (vehonroute == 0) { firstveh=true; } // everyone need at least 2 vehicle on a route
local cargowait=0;
local capacity=0;
dual = road.Twoway; // we need to check both side if source is town we're on a dual route (pass or mail)
cargowait = road.SourceStation.s_CargoProduce.GetValue(cargoid);
capacity = road.SourceStation.s_VehicleCapacity.GetValue(cargoid);
if (cStation.IsStationVirtual(road.SourceStation.s_ID)) { capacity-=cRoute.VirtualAirGroup[2]; }
if (capacity <= 0) { cargowait = road.SourceProcess.CargoProduce.GetValue(cargoid); }
capacity=futur_engine_capacity;
if (dual)
{
road.TargetStation.UpdateStationInfos();
local src_capacity=capacity;
local dst_capacity= road.TargetStation.s_VehicleCapacity.GetValue(cargoid);
local src_wait = cargowait;
local dst_wait = road.TargetStation.s_CargoProduce.GetValue(cargoid);
if (cStation.IsStationVirtual(road.TargetStation.s_ID)) { dst_capacity-=cRoute.VirtualAirGroup[2]; }
if (dst_capacity <= 0) { dst_wait = road.TargetProcess.CargoProduce.GetValue(cargoid); dst_capacity=futur_engine_capacity; }
if (src_wait < dst_wait) { cargowait=src_wait; } // keep the lowest cargo amount
else { cargowait=dst_wait; }
if (src_capacity < dst_capacity) { capacity=dst_capacity; } // but keep the highest capacity we have
else { capacity=src_capacity; }
DInfo("Source capacity="+src_capacity+" wait="+src_wait+" --- Target capacity="+dst_capacity+" wait="+dst_wait,2);
}
local remain = cargowait - capacity;
if (remain < 1) { vehneed=0; }
else { vehneed = (cargowait / capacity)+1; }
DInfo("Capacity ="+capacity+" wait="+cargowait+" remain="+remain+" needbycapacity="+vehneed,2);
if (vehneed >= vehonroute) { vehneed-=vehonroute; }
if (vehneed+vehonroute > maxveh) { vehneed=maxveh-vehonroute; }
if (AIStation.GetCargoRating(road.SourceStation.s_ID, cargoid) < 25 && vehonroute < 8) { vehneed++; }
if (firstveh)
{
if (road.CargoID == cCargo.GetPassengerCargo() && road.VehicleType != RouteType.RAIL)
{
// force 2 vehicle if none exists yet for truck/bus & aircraft / boat
if (vehneed < 2) { vehneed=2; }
}
else { vehneed=1; } // everyones else is block to 1 vehicle
if (vehneed > 8) { vehneed=8; } // max 8 at creation time
}
if (vehneed > 0)
{
local allowmax=INSTANCE.main.carrier.CanAddNewVehicle(uid, true, vehneed);
if (allowmax < vehneed) { vehneed=allowmax; }
DInfo("CanAddNewVehicle for "+road.SourceStation.s_Name+" says "+vehneed,2);
allowmax=INSTANCE.main.carrier.CanAddNewVehicle(uid, false, vehneed);
if (allowmax < vehneed) { vehneed=allowmax; }
DInfo("CanAddNewVehicle for "+road.TargetStation.s_Name+" says "+vehneed,2);
}
DInfo("Capacity="+capacity+" vehicleneed="+vehneed+" cargowait="+cargowait+" vehicule#="+road.VehicleCount+"/"+maxveh+" firstveh="+firstveh,2);
// adding vehicle
if (vehneed > 0)
{
cCarrier.RouteNeedVehicle(road.GroupID, vehneed); // we record all groups needs for vehicle
road.SourceStation.s_VehicleCapacity.SetValue(cargoid, road.SourceStation.s_VehicleCapacity.GetValue(cargoid)+(vehneed*futur_engine_capacity));
road.TargetStation.s_VehicleCapacity.SetValue(cargoid, road.TargetStation.s_VehicleCapacity.GetValue(cargoid)+(vehneed*futur_engine_capacity));
}
}
cCarrier.Process_VehicleWish();
}
DictatorAI/handler/vehiclehandler.nut 0000644 0001750 0000144 00000063175 12203702520 017230 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cCarrier::GetCurrentCargoType(vehID)
// return the cargoID in use by this vehicle
{
local cargoList=AICargoList();
foreach (cargoID, dummy in cargoList)
if (AIVehicle.GetCapacity(vehID, cargoID) > 0) return cargoID;
return -1;
}
function cCarrier::GetGroupLoadCapacity(groupID)
// return the total capacity a group of vehicle can handle
{
if (!AIGroup.IsValidGroup(groupID)) return 0;
local veh_in_group=AIVehicleList_Group(groupID);
local cargoList=AICargoList();
local total=0;
local biggest=0;
foreach (cargoID, dummy in cargoList)
{
veh_in_group.Valuate(AIVehicle.GetCapacity, cargoID);
total=0;
foreach (vehicle, capacity in veh_in_group) total+=capacity;
if (total > biggest) biggest=total;
}
return biggest;
}
function cCarrier::VehicleGetBiggestCapacityUsingStation(stationID)
// return the top capacity vehicles that use that station
{
local vehlist=AIVehicleList_Station(stationID);
vehlist.Valuate(AIEngine.GetCapacity);
vehlist.Sort(AIList.SORT_BY_VALUE,false);
local top=0;
if (!vehlist.IsEmpty()) top=vehlist.GetValue(vehlist.Begin());
return top;
}
function cCarrier::VehicleList_KeepStuckVehicle(vehicleslist)
// Filter a list of vehicle to only keep running ones with a 0 speed (stuck vehicle)
// @param vehicleslist The list of vehicle we should filter
// @return same list with only matching vehicles
{
vehicleslist.Valuate(AIVehicle.GetState);
vehicleslist.KeepValue(AIVehicle.VS_RUNNING);
vehicleslist.Valuate(AIVehicle.GetCurrentSpeed);
vehicleslist.KeepValue(0); // non moving ones
return vehicleslist;
}
function cCarrier::VehicleList_KeepLoadingVehicle(vehicleslist)
// Filter a list of vehicle to only keep ones that are loading at a station
// @param vehicleslist The list of vehicle we should filter
// @return same list with only matching vehicles
{
vehicleslist.Valuate(AIVehicle.GetState);
vehicleslist.KeepValue(AIVehicle.VS_AT_STATION);
return vehicleslist;
}
function cCarrier::VehicleNearStation(stationID)
// return a list with all road vehicles we own near that station with VS_RUNNING && VS_AT_STATION status
// @param stationID the station id to check
// @return the vehicle list
{
local vehicles = AIVehicleList_Station(stationID);
local tilelist = cTileTools.GetTilesAroundPlace(AIStation.GetLocation(stationID),24);
tilelist.Valuate(AIStation.GetStationID);
tilelist.KeepValue(stationID); // now tilelist = only the tiles of the station we were looking for
local check_tiles = AITileList();
local stationloc=AIStation.GetLocation(stationID);
foreach (tiles, stationid_found in tilelist)
{
local upper=stationloc+AIMap.GetTileIndex(-1,-1);
local lower=stationloc+AIMap.GetTileIndex(1,1);
check_tiles.AddRectangle(upper,lower);
}
local altVehicle = AIList();
foreach (vehicle, _ in vehicles)
{
if (check_tiles.HasItem(AIVehicle.GetLocation(vehicle))) altVehicle.AddItem(vehicle, AIVehicle.GetState(vehicle));
}
altVehicle.RemoveValue(AIVehicle.VS_STOPPED);
altVehicle.RemoveValue(AIVehicle.VS_IN_DEPOT);
altVehicle.RemoveValue(AIVehicle.VS_BROKEN);
altVehicle.RemoveValue(AIVehicle.VS_CRASHED);
altVehicle.RemoveValue(AIVehicle.VS_INVALID);
return altVehicle;
}
function cCarrier::VehicleGetCargoLoad(veh)
// return amout of any cargo loaded in the vehicle
{
if (!AIVehicle.IsValidVehicle(veh)) return 0;
local cargoList=AICargoList();
local amount=0;
local topamount=0;
foreach (i, dummy in cargoList)
{
amount = AIVehicle.GetCargoLoad(veh,i);
if (amount > topamount) topamount=amount;
}
return amount;
}
function cCarrier::VehicleHandleTrafficAtStation(stationID, reroute)
// if reroute this function stop all vehicles that use stationID to goto stationID
// if !rereroute this function restore vehicles orders
{
local station=cStation.Load(stationID);
if (!station) return;
local road=null;
local vehlist=null;
local veh=null;
local group=null;
local checkgroup=AIList();
checkgroup.AddList(station.s_Owner);
checkgroup.AddItem(0,0); // add pass virtual group in the list
checkgroup.AddItem(1,0); // add mail virtual
foreach (ownID, dummy in checkgroup)
{
road = cRoute.Load(ownID);
if (!road || road.Status != 100) continue;
if (reroute)
{
vehlist = AIVehicleList_Group(road.GroupID);
vehlist.Valuate(AIVehicle.GetState);
vehlist.RemoveValue(AIVehicle.VS_STOPPED);
vehlist.RemoveValue(AIVehicle.VS_IN_DEPOT);
vehlist.RemoveValue(AIVehicle.VS_CRASHED);
vehlist.RemoveList(cCarrier.ToDepotList); // remove vehicle on their way to depot
if (vehlist.IsEmpty()) continue;
veh = vehlist.Begin();
local orderindex=VehicleFindDestinationInOrders(veh, stationID);
if (orderindex != -1)
{
DInfo("Re-routing traffic on route "+road.Name+" to ignore "+station.s_Name,0);
if (!AIOrder.RemoveOrder(veh, AIOrder.ResolveOrderPosition(veh, orderindex)))
{ DError("Fail to remove order for vehicle "+INSTANCE.main.carrier.GetVehicleName(veh),2); }
}
}
else { INSTANCE.main.carrier.VehicleBuildOrders(road.GroupID,true); }
}
}
function cCarrier::VehicleSendToDepot_GetReason(reason)
// return the real reason why a vehicle is in SendToDepot list
{
if (reason >= DepotAction.ADDWAGON && reason < DepotAction.LINEUPGRADE) return DepotAction.ADDWAGON;
if (reason >= DepotAction.LINEUPGRADE && reason < DepotAction.SIGNALUPGRADE) return DepotAction.LINEUPGRADE;
if (reason >= DepotAction.SIGNALUPGRADE && reason < DepotAction.WAITING) return DepotAction.SIGNALUPGRADE;
if (reason >= DepotAction.WAITING && reason < DepotAction.WAITING+200) return DepotAction.WAITING;
return reason;
}
function cCarrier::VehicleSendToDepot_GetParam(reason)
// return the parameter found inside the reason or -1 if it's not valid
{
if (reason >= DepotAction.ADDWAGON && reason < DepotAction.LINEUPGRADE) return (reason - DepotAction.ADDWAGON);
if (reason >= DepotAction.LINEUPGRADE && reason < DepotAction.SIGNALUPGRADE) return (reason - DepotAction.LINEUPGRADE);
if (reason >= DepotAction.SIGNALUPGRADE && reason < DepotAction.WAITING) return (reason - DepotAction.SIGNALUPGRADE);
if (reason >= DepotAction.WAITING && reason < DepotAction.WAITING+200) return (reason - DepotAction.WAITING);
return -1;
}
function cCarrier::VehicleSendToDepot(veh,reason)
// send a vehicle to depot
{
if (!AIVehicle.IsValidVehicle(veh)) return false;
local real_reason = cCarrier.VehicleSendToDepot_GetReason(reason);
if (INSTANCE.main.carrier.ToDepotList.HasItem(veh))
{
if (reason < DepotAction.LINEUPGRADE) return false;
// ignore order if we already have one, but not ignoring LINEUPGRADE, SIGNALUPGRADE or WAITING to crush previous one
}
if (AIVehicle.GetVehicleType(veh) == AIVehicle.VT_RAIL) INSTANCE.main.carrier.TrainSetDepotOrder(veh);
else INSTANCE.main.carrier.VehicleSetDepotOrder(veh);
local understood=false;
local target=AIOrder.GetOrderDestination(veh, AIOrder.ORDER_CURRENT);
local dist=AITile.GetDistanceManhattanToTile(AIVehicle.GetLocation(veh), target);
AIController.Sleep(6); // wait it to move a bit
local newtake=AITile.GetDistanceManhattanToTile(AIVehicle.GetLocation(veh), target);
if (AIVehicle.GetVehicleType(veh)!=AIVehicle.VT_RAIL && newtake > dist)
{
DInfo("Reversing direction of "+INSTANCE.main.carrier.GetVehicleName(veh),1);
AIVehicle.ReverseVehicle(veh);
}
local rr="";
local wagonnum = cCarrier.VehicleSendToDepot_GetParam(reason);
switch (real_reason)
{
case DepotAction.SELL:
rr="to be sold.";
break;
case DepotAction.LINEUPGRADE:
rr="to change railtype";
break;
case DepotAction.SIGNALUPGRADE:
rr="to upgrade signals";
break;
case DepotAction.WAITING:
rr="to wait";
break;
case DepotAction.UPGRADE:
rr="to be upgrade.";
break;
case DepotAction.REPLACE:
rr="to be replace.";
break;
case DepotAction.CRAZY:
rr="for a crazy action.";
break;
case DepotAction.REMOVEROUTE:
rr="for removing a dead route.";
break;
case DepotAction.ADDWAGON:
rr="to add "+wagonnum+" new wagons.";
break;
}
DInfo("Vehicle "+INSTANCE.main.carrier.GetVehicleName(veh)+" is going to depot "+rr,0);
INSTANCE.main.carrier.ToDepotList.AddItem(veh,reason);
}
function cCarrier::VehicleGetFullCapacity(veh)
// return total capacity a vehicle can handle
{
if (!AIVehicle.IsValidVehicle(veh)) return -1;
local mod=AIVehicle.GetVehicleType(veh);
local engine=AIVehicle.GetEngineType(veh);
if (mod == AIVehicle.VT_RAIL)
{ // trains
local wagonnum=AIVehicle.GetNumWagons(veh);
local wagonengine=AIVehicle.GetWagonEngineType(veh,1);
local wagoncapacity=AIEngine.GetCapacity(wagonengine);
local traincapacity=AIEngine.GetCapacity(engine);
local total=traincapacity+(wagonnum*wagoncapacity);
return total;
}
else { // others
local value=AIEngine.GetCapacity(engine);
return value;
}
}
function cCarrier::VehicleFindRouteIndex(veh)
// return UID of the route the veh vehicle is running on
{
local group=AIVehicle.GetGroupID(veh);
if (cRoute.GroupIndexer.HasItem(group)) return cRoute.GroupIndexer.GetValue(group);
return null;
}
function cCarrier::VehicleUpgradeEngine(vehID)
// we will try to upgrade engine and wagons for vehicle veh
{
local idx=INSTANCE.main.carrier.VehicleFindRouteIndex(vehID);
local oldenginename=cCarrier.GetVehicleName(vehID);
if (idx == null)
{
DWarn("This vehicle "+oldenginename+" is not use by any route !!!",1);
INSTANCE.main.carrier.VehicleSell(vehID, false);
return false;
}
local betterEngine=cEngine.IsVehicleAtTop(vehID);
local vehtype=AIVehicle.GetVehicleType(vehID);
local new_vehID=null;
local road=cRoute.Load(idx);
if (!road) { cCarrier.VehicleSell(vehID, false); return false; }
local betterEngine=cEngine.IsVehicleAtTop(vehID);
if (betterEngine == -1)
{
DWarn("That vehicle have its engine already at top",1);
//cCarrier.VehicleBuildOrders(road.GroupID,false);
//cCarrier.StartVehicle(vehID);
//return false;
}
local homedepot=cRoute.GetDepot(idx);
if (homedepot==-1) homedepot=AIVehicle.GetLocation(vehID);
DInfo("Upgrading using depot at "+homedepot,2);
local money=0;
switch (vehtype)
{
case AIVehicle.VT_RAIL:
// Upgrading the loco engine is doable, but it might get too complexe for nothing, so i will destroy the train, and X more wagons
local numwagon = cEngineLib.GetNumberOfWagons(vehID);
cCarrier.VehicleSell(vehID, false);
DInfo("Train vehicle "+oldenginename+" removed, a new train will be built with "+numwagon+" wagons",0);
cCarrier.ForceAddTrain(idx, numwagon);
return;
break;
case AIVehicle.VT_ROAD:
cCarrier.VehicleSell(vehID,false);
new_vehID = cEngineLib.CreateVehicle(homedepot, betterEngine, road.CargoID);
break;
case AIVehicle.VT_AIR:
cCarrier.VehicleSell(vehID,false);
new_vehID = cEngineLib.CreateVehicle(homedepot, betterEngine, road.CargoID);
break;
case AIVehicle.VT_WATER:
cCarrier.VehicleSell(vehID,false);
return;
break;
}
if (AIVehicle.IsValidVehicle(new_vehID))
{
local newenginename=INSTANCE.main.carrier.GetVehicleName(new_vehID);
AIGroup.MoveVehicle(road.GroupID,new_vehID);
DInfo("Vehicle "+oldenginename+" replace with "+newenginename,0);
cCarrier.StartVehicle(new_vehID); // Not sharing orders with previous vehicle as its orders are "goto depot" orders
cCarrier.VehicleBuildOrders(road.GroupID,false); // need to build its orders
}
}
function cCarrier::VehicleMaintenance_Orders(vehID)
// try to repair orders for a vehicle, else send it to depot
{
local numorders=AIOrder.GetOrderCount(vehID);
local name=cCarrier.GetVehicleName(vehID);
for (local z=AIOrder.GetOrderCount(vehID)-1; z >=0; z--)
{ // I check backward to prevent z index gone wrong if an order is remove
if (!INSTANCE.main.carrier.VehicleOrderIsValid(vehID, z))
{
DInfo("-> Vehicle "+name+" have invalid order, removing orders "+z,0);
AIOrder.RemoveOrder(vehID, z);
}
}
numorders=AIOrder.GetOrderCount(vehID);
if (numorders < 2)
{
local groupid=AIVehicle.GetGroupID(vehID);
DInfo("-> Vehicle "+name+" have too few orders, trying to correct it",0);
INSTANCE.main.carrier.VehicleBuildOrders(groupid,false);
}
numorders=AIOrder.GetOrderCount(vehID);
if (numorders < 2)
{
DInfo("-> Vehicle "+name+" have too few orders, sending it to depot",0);
INSTANCE.main.carrier.VehicleSendToDepot(vehID, DepotAction.SELL);
cCarrier.CheckOneVehicleOrGroup(vehID,true); // push all vehicles to get a check
}
}
function cCarrier::IsTrainRouteBusy(uid = -1)
{
local grp = null;
local l = AIList();
l.AddList(cCarrier.ToDepotList);
foreach (veh, reason in l)
{
if (!AIVehicle.IsValidVehicle(veh)) { cCarrier.ToDepotList.RemoveItem(veh); continue; }
if (uid != -1) {
grp = cCarrier.VehicleFindRouteIndex(veh);
if (grp != uid) continue;
}
local real = cCarrier.VehicleSendToDepot_GetReason(reason);
if (real == DepotAction.LINEUPGRADE || real == DepotAction.SIGNALUPGRADE) return true;
}
return false;
}
function cCarrier::VehicleMaintenance()
// lookout our vehicles for troubles
{
local tlist=AIList();
while (cCarrier.MaintenancePool.len()>0) tlist.AddItem(cCarrier.MaintenancePool.pop(),0);
// Get the work and clean the mainteance list
tlist.Valuate(AIVehicle.GetState);
tlist.RemoveValue(AIVehicle.VS_STOPPED);
tlist.RemoveValue(AIVehicle.VS_IN_DEPOT);
tlist.RemoveValue(AIVehicle.VS_CRASHED);
tlist.RemoveValue(AIVehicle.VS_INVALID);
DInfo("Checking "+tlist.Count()+" vehicles",0);
local name="";
local tx, ty, price=0; // temp variable to use freely
INSTANCE.main.carrier.warTreasure=0;
local allroadveh=AIVehicleList();
allroadveh.Valuate(AIVehicle.GetState);
allroadveh.RemoveValue(AIVehicle.VS_STOPPED);
allroadveh.RemoveValue(AIVehicle.VS_CRASHED);
allroadveh.RemoveValue(AIVehicle.VS_INVALID);
allroadveh.RemoveValue(AIVehicle.VS_IN_DEPOT);
local checkallvehicle=(allroadveh.Count()==tlist.Count());
if (checkallvehicle)
{
allroadveh.Valuate(AIVehicle.GetVehicleType);
allroadveh.KeepValue(AIVehicle.VT_ROAD);
}
local line_upgrade = !cCarrier.IsTrainRouteBusy();
foreach (vehicle, dummy in tlist)
{
cCarrier.VehicleMaintenance_Orders(vehicle);
local vehtype=AIVehicle.GetVehicleType(vehicle);
if (vehtype == AIVehicle.VT_ROAD) INSTANCE.main.carrier.warTreasure+=AIVehicle.GetCurrentValue(vehicle);
local topengine=cEngine.IsVehicleAtTop(vehicle);
if (topengine != -1) price=cEngine.GetPrice(topengine);
else price=cEngine.GetPrice(AIVehicle.GetEngineType(vehicle));
name=INSTANCE.main.carrier.GetVehicleName(vehicle);
if (vehtype == AIVehicle.VT_RAIL && line_upgrade)
{ // check train can use better rails
local ret = RailFollower.TryUpgradeLine(vehicle);
if (ret == 0) { line_upgrade = false; continue; }
if (ret == 1) { continue; }
}
tx=AIVehicle.GetAgeLeft(vehicle);
if (tx < cCarrier.OldVehicle)
{
if (!cBanker.CanBuyThat(price+INSTANCE.main.carrier.vehicle_cash)) { continue; }
DInfo("-> Vehicle "+name+" is getting old ("+tx+" days left), replacing it",0);
INSTANCE.main.carrier.VehicleSendToDepot(vehicle,DepotAction.REPLACE);
cCarrier.CheckOneVehicleOrGroup(vehicle, true);
INSTANCE.main.carrier.vehicle_cash += price;
continue;
}
tx=INSTANCE.main.carrier.VehicleGetProfit(vehicle);
ty=AIVehicle.GetAge(vehicle);
if (ty > 240 && tx < 0 && INSTANCE.OneMonth > 6) // (6 months after new year)
{
ty = cCarrier.VehicleFindRouteIndex(vehicle);
//cBuilder.RouteIsDamage(ty);
}
tx=AIVehicle.GetReliability(vehicle);
if (tx < 30)
{
DInfo("-> Vehicle "+name+" reliability is low ("+tx+"%), sending it for servicing at depot",0);
AIVehicle.SendVehicleToDepotForServicing(vehicle);
local idx = cCarrier.VehicleFindRouteIndex(vehicle);
//cBuilder.RouteIsDamage(idx);
cCarrier.CheckOneVehicleOrGroup(vehicle, true);
}
local enginecheck = cEngine.IsRabbitSet(vehicle);
if (topengine != -1 && enginecheck) topengine=-1; // stop upgrade
if (topengine != -1)
{
// reserving money for the upgrade
DInfo("Upgrade engine ! "+cBanker.CanBuyThat(INSTANCE.main.carrier.vehicle_cash+price)+" price: "+price,4);
if (!cBanker.CanBuyThat(INSTANCE.main.carrier.vehicle_cash + price)) continue; // no way, we lack funds for it
INSTANCE.main.carrier.vehicle_cash += price;
DInfo("-> Vehicle "+name+" can be upgrade with a better version, sending it to depot",0);
cEngine.RabbitSet(vehicle);
cCarrier.VehicleSendToDepot(vehicle, DepotAction.UPGRADE);
cCarrier.CheckOneVehicleOrGroup(vehicle, true);
}
local pause = cLooper();
}
if (!checkallvehicle)
{ // we need to estimate the fleet value
local midvalue=0;
if (allroadveh.Count() > 0) midvalue=INSTANCE.main.carrier.warTreasure / allroadveh.Count();
local fleet = allroadveh.Count()-6;
if (fleet < 0) fleet=0;
INSTANCE.main.carrier.warTreasure = fleet * midvalue;
DInfo("warTreasure estimate to "+INSTANCE.main.carrier.warTreasure+" fleet: "+fleet,2);
}
}
function cCarrier::CrazySolder(moneytoget)
// this function send & sold nearly all road vehicle to get big money back
{
local allvehicle=AIVehicleList();
allvehicle.Valuate(AIVehicle.GetVehicleType);
allvehicle.KeepValue(AIVehicle.VT_ROAD);
allvehicle.Valuate(AIVehicle.GetProfitThisYear);
allvehicle.Sort(AIList.SORT_BY_VALUE, false);
allvehicle.RemoveTop(2);
allvehicle.Sort(AIList.SORT_BY_VALUE, true);
foreach (vehicle, dummy in allvehicle)
{
local c = cLooper();
INSTANCE.main.carrier.VehicleSendToDepot(vehicle,DepotAction.CRAZY);
if (moneytoget < 0) break;
moneytoget -= AIVehicle.GetCurrentValue(vehicle);
}
}
function cCarrier::VehicleSell(veh, recordit)
// sell the vehicle and update route info
{
DInfo("Selling Vehicle "+INSTANCE.main.carrier.GetVehicleName(veh),0);
local uid=INSTANCE.main.carrier.VehicleFindRouteIndex(veh);
local road=cRoute.Load(uid);
local vehvalue=AIVehicle.GetCurrentValue(veh);
local vehtype=AIVehicle.GetVehicleType(veh);
INSTANCE.main.carrier.vehicle_cash -= vehvalue;
if (INSTANCE.main.carrier.vehicle_cash < 0) INSTANCE.main.carrier.vehicle_cash = 0;
if (AIVehicle.GetVehicleType(veh)==AIVehicle.VT_RAIL) cTrain.DeleteVehicle(veh); // must be call before selling the vehicle
cEngine.RabbitUnset(veh);
AIVehicle.SellVehicle(veh);
if (!road) return;
road.RouteUpdateVehicle();
if (recordit) road.DateVehicleDelete=AIDate.GetCurrentDate();
}
function cCarrier::VehicleSendToDepotAndSell(uid)
// Send and sell all vehicles from route uid, used by checks.nut to repair road
{
local road=cRoute.Load(uid);
if (!road) return;
local vehlist = AIVehicleList_Group(road.GroupID);
if (!vehlist.IsEmpty()) foreach (veh, _ in vehlist) INSTANCE.main.carrier.VehicleSendToDepot(veh, DepotAction.SELL);
}
function cCarrier::FreeDepotOfVehicle(depotID)
// this function remove any vehicle in depot
{
if (!cStation.IsDepot(depotID)) { return true; }
DInfo("Selling all vehicles at depot "+depotID+" to remove it.",2);
local vehlist = AIVehicleList();
vehlist.Valuate(AIVehicle.GetLocation);
vehlist.KeepValue(depotID);
if (vehlist.IsEmpty()) { return true; }
foreach (veh, _ in vehlist) { cCarrier.VehicleSell(veh, false); local pause = cLooper(); }
vehlist.Valuate(AIVehicle.IsValidVehicle);
vehlist.RemoveValue(1);
return (vehlist.IsEmpty());
}
function cCarrier::VehicleIsWaitingInDepot(onlydelete=false)
// this function checks our depots and sell vehicle in it
// if onlydelete set to true, we only remove vehicle, no upgrade/replace...
{
local tlist=AIVehicleList();
tlist.Valuate(AIVehicle.IsStoppedInDepot);
tlist.KeepValue(1);
DInfo("Checking vehicles in depots: "+tlist.Count(),2);
foreach (i, dummy in tlist)
{
local kk = cLooper();
if (!AIVehicle.IsValidVehicle(i)) continue;
local reason=DepotAction.SELL;
local parameter=0;
local uid=0;
local name=INSTANCE.main.carrier.GetVehicleName(i);
INSTANCE.main.carrier.VehicleOrdersReset(i);
if (INSTANCE.main.carrier.ToDepotList.HasItem(i))
{
reason=INSTANCE.main.carrier.ToDepotList.GetValue(i);
INSTANCE.main.carrier.ToDepotList.RemoveItem(i);
parameter = cCarrier.VehicleSendToDepot_GetParam(reason);
reason = cCarrier.VehicleSendToDepot_GetReason(reason);
if (reason == DepotAction.ADDWAGON)
{
uid=INSTANCE.main.carrier.VehicleFindRouteIndex(i);
if (uid==null) {
DError("Cannot find the route uid for "+name,2);
reason = DepotAction.SELL;
}
}
if (reason == DepotAction.SIGNALUPGRADE)
{
local invalid = false;
if (!AIStation.IsValidStation(parameter)) invalid = true;
else {
uid = INSTANCE.main.carrier.VehicleFindRouteIndex(i);
if (uid == null) invalid = true;
else {
uid = cRoute.Load(uid);
if (!uid) invalid=true;
}
}
if (invalid) {
reason = DepotAction.SELL;
DError("Invalid stationID pass to SIGNALUPGRADE = "+parameter);
}
}
}
else {
if (AIVehicle.GetVehicleType(i)==AIVehicle.VT_RAIL)
{
DInfo("I don't know the reason why "+name+" is at depot, restarting it",1);
cCarrier.TrainExitDepot(i);
continue;
}
else DInfo("I don't know the reason why "+name+" is at depot, selling it",1);
}
if (onlydelete && (AIVehicle.GetVehicleType(i) == AIVehicle.VT_AIR || AIVehicle.GetVehicleType(i) == AIVehicle.VT_ROAD))
{ DInfo("We've been ask to delete all vehicles waiting in depot",1); reason=DepotAction.CRAZY; }
switch (reason)
{
case DepotAction.SELL:
DInfo("Vehicle "+name+" is waiting in depot to be sold",1);
INSTANCE.main.carrier.VehicleSell(i,true);
break;
case DepotAction.UPGRADE:
DInfo("Vehicle "+name+" is waiting in depot to be upgrade",1);
INSTANCE.main.carrier.VehicleUpgradeEngine(i);
break;
case DepotAction.REPLACE:
DInfo("Vehicle "+name+" is waiting in depot to be replace",1);
//INSTANCE.main.carrier.VehicleSell(i,false);
INSTANCE.main.carrier.VehicleUpgradeEngine(i);
break;
case DepotAction.CRAZY:
INSTANCE.main.carrier.VehicleSell(i,false);
break;
case DepotAction.REMOVEROUTE:
INSTANCE.main.carrier.VehicleSell(i, false);
break;
case DepotAction.ADDWAGON:
DInfo("Vehicle "+name+" is waiting at depot to get "+parameter+" wagons",1);
INSTANCE.main.carrier.AddWagon(uid, parameter);
break;
case DepotAction.LINEUPGRADE:
cCarrier.ToDepotList.AddItem(i, DepotAction.LINEUPGRADE);
local all_vehicle = AIList();
all_vehicle.AddList(cCarrier.ToDepotList);
local good_vehicle = AIList();
foreach (veh, reason in all_vehicle)
{
local real_reason = cCarrier.VehicleSendToDepot_GetReason(reason);
if (real_reason == DepotAction.LINEUPGRADE) good_vehicle.AddItem(veh, AIVehicle.GetState(veh));
}
local runnercount = good_vehicle.Count();
good_vehicle.KeepValue(AIVehicle.VS_IN_DEPOT);
runnercount -= good_vehicle.Count();
if (runnercount == 0) { RailFollower.TryUpgradeLine(i); }
else { DInfo("Waiting "+runnercount+" more trains to upgrade line.",1); }
break;
case DepotAction.WAITING:
DInfo("Vehicle "+name+" is waiting at depot for "+parameter+" times",1);
parameter--;
if (parameter <= 0)
{
DInfo("Vehicle "+name+" has wait enough, releasing it to the wild",1);
cCarrier.StartVehicle(i);
}
else INSTANCE.main.carrier.ToDepotList.AddItem(i, DepotAction.WAITING + parameter);
break;
case DepotAction.SIGNALUPGRADE:
local vehlist = AIVehicleList_Station(parameter);
vehlist.Valuate(AIVehicle.GetState);
vehlist.RemoveValue(AIVehicle.VS_IN_DEPOT);
if (vehlist.IsEmpty()) {
// don't care result, it's just to let the station build its signals
cBuilder.RailStationGrow(uid.TargetStation.s_ID, uid.Target_RailEntry, false);
// but we must add a non taker train, and do it to the target station to prevent station growing while making that
// as trains are currently stopped waiting for the signal to be made, it will cause hell if station try to grow and
// so cannot remove depot
}
else {
DInfo("Waiting "+vehlist.Count()+" more vehicles to stop at depot",1);
INSTANCE.main.carrier.ToDepotList.AddItem(i, DepotAction.WAITING+50); // making it wait to not get stuck forever
}
break;
}
}
}
function cCarrier::TrainExitDepot(vehID)
// release a train that was in depot, setting its order, starting it and moving it to the best station
{
if (!AIVehicle.GetVehicleType(vehID) == AIVehicle.VT_RAIL || !AIVehicle.GetState(vehID) == AIVehicle.VS_IN_DEPOT) return;
local loaded=cCarrier.VehicleGetCargoLoad(vehID);
INSTANCE.main.carrier.TrainSetOrders(vehID);
if (loaded > 0) AIOrder.SkipToOrder(vehID, 1);
else AIOrder.SkipToOrder(vehID, 0);
cCarrier.StartVehicle(vehID);
if (INSTANCE.main.carrier.ToDepotList.HasItem(vehID)) INSTANCE.main.carrier.ToDepotList.RemoveItem(vehID);
}
function cCarrier::StartVehicle(vehID)
// This try to make sure we will start the vehicle and not stop it because of the weak cCarrier.StartVehicle function
{
if (!AIVehicle.IsValidVehicle(vehID)) return false;
local wait=false;
while (AIVehicle.GetState(vehID) == AIVehicle.VS_BROKEN) { wait=true; AIController.Sleep(15); }
if (wait) AIController.Sleep(40); // wait a few it just restart
local state = AIVehicle.GetState(vehID);
if (state == AIVehicle.VS_STOPPED || state == AIVehicle.VS_IN_DEPOT)
{
AIVehicle.StartStopVehicle(vehID);
DInfo("Starting "+cCarrier.GetVehicleName(vehID)+"...",0);
return true;
}
return false; // crash/invalid...
}
function cCarrier::StopVehicle(vehID)
// Try to stop a vehicle that is running, and not restart it...
{
if (AIVehicle.GetState(vehID) == AIVehicle.VS_RUNNING && AIVehicle.StartStopVehicle(vehID))
{
DInfo("Stopping "+cCarrier.GetVehicleName(vehID)+"...",0);
return true;
}
return false;
}
DictatorAI/handler/jobs.nut 0000644 0001750 0000144 00000066505 12203302246 015211 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cJobs::Load(UID)
// Load a job with some check against bad job
{
local obj = cJobs.GetJobObject(UID);
if (obj == null)
{
DWarn("cJobs.Load function return NULL with UID="+UID,3);
return false;
}
return obj;
}
function cJobs::ReuseTownSet(townID)
// Add that town as a target town so we derank it, to avoid re-using this town too much
{
if (cJobs.TownAbuse.HasItem(townID)) { cJobs.TownAbuse.SetValue(townID, cJobs.TownAbuse.GetValue(townID)+1); }
else { cJobs.TownAbuse.AddItem(townID, 1); }
}
function cJobs::CheckLimitedStatus()
// Check & set the limited status, at early stage we limit the distance to accept a job.
{
local oldmax=distanceLimits[1];
local testLimitChange= GetTransportDistance(RouteType.RAIL, false, INSTANCE.main.bank.unleash_road); // get max distance a train could do
if (oldmax != distanceLimits[1])
{
DInfo("Distance limit status change to "+INSTANCE.main.bank.unleash_road,4);
}
}
function cJobs::Save()
// save the job
{
local dualrouteavoid=cJobs();
dualrouteavoid.UID=null;
dualrouteavoid.sourceObject = this.targetObject; // swap source and target
dualrouteavoid.targetObject = this.sourceObject;
dualrouteavoid.roadType=this.roadType;
dualrouteavoid.cargoID=this.cargoID;
dualrouteavoid.GetUID();
if (dualrouteavoid.UID in database) // this remove cases where Paris->Nice(pass/bus) Nice->Paris(pass/bus)
{
DInfo("Job "+this.UID+" is a dual route. Dropping job",4);
dualrouteavoid=null;
return ;
}
dualrouteavoid=null;
local jobinfo=cCargo.GetCargoLabel(this.cargoID)+"-"+cRoute.RouteTypeToString(this.roadType)+" "+this.distance+"m from ";
jobinfo+=this.sourceObject.Name;
jobinfo+=" to ";
jobinfo+=this.targetObject.Name;
this.Name=jobinfo;
if (this.UID in database) { DInfo("Job "+this.UID+" already in database",4); }
else {
DInfo("Adding job #"+this.UID+" ("+parentID+") to job database: "+jobinfo,4);
database[this.UID] <- this;
cJobs.jobIndexer.AddItem(this.UID, 0);
}
}
function cJobs::GetUID()
// Create a UID and parentID for a job
// Return the UID for that job
{
local uID=null;
local parentID = null;
if (this.UID == null && this.sourceObject.ID != null && this.targetObject.ID != null && this.cargoID != null && this.roadType != null)
{
local v1=this.roadType+1;
local v2=(this.cargoID+15);
local v3=(this.targetObject.ID+100);
if (this.targetObject.IsTown) { v3+=1000; }
local v4=(this.sourceObject.ID+10000);
if (this.sourceObject.IsTown) { v4+=4000; }
parentID= v4+(this.cargoID+1);
if (this.roadType == RouteType.AIR) { parentID = v4+(this.cargoID+100); }
if (this.roadType == RouteType.ROAD && this.cargoID == cCargo.GetPassengerCargo())
{ parentID = v4+(this.cargoID+300); }
// parentID: prevent a route done by a transport to be done by another transport
// As paris->anywhere(v/bus)[parentID=1000] paris->anywhere(pass/train)[parentID=1000]
// the aircraft and bus different ID means they could always be build, even a bus/aircraft is doing the job already
uID = (v3*v4)+(v1*v2);
this.UID=uID;
this.parentID=parentID;
//DInfo("JOBS -> "+uID+" src="+this.sourceID+" tgt="+this.targetID+" crg="+this.cargoID+" rt="+this.roadType);
}
return this.UID;
}
function cJobs::RankThisJob()
// rank the current job
{
local srcTown = this.sourceObject.IsTown;
local dstTown = this.targetObject.IsTown;
this.cargoAmount = this.sourceObject.ScoreProduction;
if (srcTown && dstTown) { this.cargoAmount= ((this.sourceObject.ScoreProduction + this.targetObject.ScoreProduction) / 2); }
local valuerank = this.cargoAmount * this.cargoValue;
if (this.subsidy)
{
if (AIGameSettings.IsValid("subsidy_multiplier")) { valuerank=valuerank * AIGameSettings.GetValue("subsidy_multiplier"); }
else { valuerank=valuerank * 2; }
}
// grant a bonus to subsidy
local stationrank = this.sourceObject.ScoreRating * this.targetObject.ScoreRating;
if (this.cargoID == cCargo.GetPassengerCargo() || this.cargoID == cCargo.GetMailCargo)
{
local src_drank = 0;
local dst_drank = 0;
if (srcTown && cJobs.TownAbuse.HasItem(this.sourceObject.ID))
{
if (this.roadType == RouteType.RAIL) { src_drank = valuerank; }
else { src_drank = ((20 * valuerank) / 100) * cJobs.TownAbuse.GetValue(this.sourceObject.ID); }
DInfo("Downranking because "+AITown.GetName(this.sourceObject.ID)+" is already use : Lost "+src_drank,4);
}
if (dstTown && cJobs.TownAbuse.HasItem(this.targetObject.ID))
{
if (this.roadType == RouteType.RAIL) { dst_drank = valuerank; }
else { dst_drank = ((20 * valuerank) / 100) * cJobs.TownAbuse.GetValue(this.targetObject.ID); }
DInfo("Downranking because "+AITown.GetName(this.targetObject.ID)+" is already use : Lost "+dst_drank,4);
}
valuerank -= (src_drank + dst_drank);
}
if (stationrank < 1 || valuerank < 1)
{ this.ranking = 0; }
else { this.ranking = stationrank + valuerank; }
}
function cJobs::RefreshValue(jobID, updateCost=false)
// refresh the datas from object
{
if (cJobs.IsInfosUpdate(jobID)) { DInfo("JobID: "+jobID+" infos are fresh",3); return null; }
else { DInfo("JobID: "+jobID+" refreshing infos",3); }
cJobs.jobIndexer.SetValue(jobID,AIDate.GetCurrentDate());
if (jobID == 0 || jobID == 1) { return null; } // don't refresh virtual routes
local myjob = cJobs.Load(jobID);
if (!myjob) { return null; }
local badind=false;
// avoid handling a dead industry we didn't get the event yet
if (!myjob.sourceObject.IsTown && !AIIndustry.IsValidIndustry(myjob.sourceObject.ID))
{
badind=true;
cJobs.MarkIndustryDead(myjob.sourceObject.ID);
}
if (!myjob.targetObject.IsTown && !AIIndustry.IsValidIndustry(myjob.targetObject.ID))
{
badind=true;
cJobs.MarkIndustryDead(myjob.targetObject.ID);
}
if (badind)
{
DInfo("Removing bad industry from the job pool: "+myjob.UID,3);
local deadroute=cRoute.Load(myjob.UID);
if (!deadroute) { return; }
DInfo("RefreshValue mark "+deadroute.UID+" undoable",1);
deadroute.RouteIsNotDoable();
return;
}
if (myjob.isUse) { return; } // no need to refresh an already done job
local pause = cLooper();
// moneyGains, ranking & cargoAmount
myjob.sourceObject.UpdateScore();
myjob.targetObject.UpdateScore();
if (updateCost) { myjob.EstimateCost(); }
myjob.RankThisJob();
}
function cJobs::IsInfosUpdate(jobID)
// return true if jobID info is recent, false if we need to refresh it
// we also use this one as valuator
{
local now=AIDate.GetCurrentDate();
local jdate=null;
if (cJobs.jobIndexer.HasItem(jobID)) { jdate=cJobs.jobIndexer.GetValue(jobID); }
else { return true; } // if job is not index return infos are fresh
return ( (now-jdate) < 7) ? true : false;
}
function cJobs::QuickRefresh()
// refresh datas on first 5 doable top jobs
{
local smallList=AIList();
INSTANCE.main.jobs.UpdateDoableJobs();
smallList.AddList(cJobs.jobIndexer);
local now = AIDate.GetCurrentDate();
now = now - 30;
smallList.RemoveList(cRoute.RouteIndexer); // remove jobs already in use
smallList.KeepBelowValue(now);
smallList.KeepTop(10); // refresh 10 random jobs that need a refresh
foreach (smallID, dvalue in smallList) { INSTANCE.main.jobs.RefreshValue(smallID, true); }
smallList.Clear();
smallList.AddList(cJobs.jobDoable);
smallList.Sort(AIList.SORT_BY_VALUE, false);
if (INSTANCE.safeStart > 0 && smallList.IsEmpty()) { INSTANCE.safeStart=0; } // disable it if we cannot find any jobs
return smallList;
}
function cJobs::GetRanking(jobID)
// return the ranking for jobID
{
local myjob = cJobs.Load(jobID);
if (!myjob) { return 0; }
return myjob.ranking;
}
function cJobs::GetNextJob()
// Return the next job UID to do, -1 if we have none to do
{
local smallList=QuickRefresh();
if (smallList.IsEmpty()) { DInfo("Can't find any good jobs to do",1); return -1; }
else { DInfo("Doable jobs: "+smallList.Count(),1); }
return smallList.Begin();
}
function cJobs::EstimateCost()
// Estimate the cost to build a job
{
local money = 0;
local clean= AITile.GetBuildCost(AITile.BT_CLEAR_ROCKY)*cBanker.GetInflationRate();
local engine=0;
local engineprice=0;
local daystransit=0;
switch (this.roadType)
{
case RouteType.ROAD:
// 2 vehicle + 2 stations + 2 depot + 4 destuction + 4 road for entry and length*road
engine=cEngine.GetEngineByCache(RouteType.ROAD, this.cargoID);
if (engine != -1) { engineprice=cEngine.GetPrice(engine); }
else { engineprice=100000; }
money+=engineprice;
money+=2*(AIRoad.GetBuildCost(AIRoad.ROADTYPE_ROAD, AIRoad.BT_TRUCK_STOP));
money+=2*(AIRoad.GetBuildCost(AIRoad.ROADTYPE_ROAD, AIRoad.BT_DEPOT));
money+=4*clean;
money+=(4+distance)*(AIRoad.GetBuildCost(AIRoad.ROADTYPE_ROAD, AIRoad.BT_ROAD));
daystransit=16;
break;
case RouteType.RAIL:
// 1 vehicle + 2 stations + 2 depot + 4 destuction + 12 tracks entries and length*rail
local rtype=null;
engine=cEngine.GetEngineByCache(RouteType.CHOPPER+1, this.cargoID);
if (engine != -1)
{
engineprice+=cEngine.GetPrice(engine);
rtype=cEngineLib.GetBestRailType(engine);
if (rtype==-1) { rtype=null; }
}
else { engineprice=500000; }
money+=engineprice;
money+=(8*clean);
if (rtype==null) { money+=500000; }
else {
money+=((20+distance)*(AIRail.GetBuildCost(rtype, AIRail.BT_TRACK)));
money+=((5)*(AIRail.GetBuildCost(rtype, AIRail.BT_STATION))); // station train 5 length
money+=(2*(AIRail.GetBuildCost(rtype, AIRail.BT_DEPOT)));
}
daystransit=4;
break;
case RouteType.WATER:
// 2 vehicle + 2 stations + 2 depot
engine = cEngine.GetEngineByCache(RouteType.WATER, this.cargoID);
if (engine != null) { engineprice=cEngine.GetPrice(engine); }
else { engineprice=500000; }
money+=engineprice*2;
money+=2*(AIMarine.GetBuildCost(AIMarine.BT_DOCK));
money+=2*(AIMarine.GetBuildCost(AIMarine.BT_DEPOT));
daystransit=32;
break;
case RouteType.AIR:
// 2 vehicle + 2 airports
engine=cEngine.GetEngineByCache(RouteType.AIR, RouteType.AIR);
if (engine != -1) { engineprice=cEngine.GetPrice(engine); }
else { engineprice=500000; }
money+=engineprice*2;
money+=2*(AIAirport.GetPrice(INSTANCE.main.builder.GetAirportType()));
daystransit=6;
break;
}
this.moneyToBuild=money;
this.cargoValue=AICargo.GetCargoIncome(this.cargoID, this.distance, daystransit);
DInfo("moneyToBuild="+this.moneyToBuild+" Income: "+this.cargoValue,4);
}
function cJobs::GetTransportDistance(transport_type, get_min, limited)
// Return the transport distance a transport_type could do
// get_min = true return minimum distance
// get_min = false return maximum distance
{
local small=1000;
local big=0;
local target=transport_type * 3;
local toret=0;
for (local i=0; i < TRANSPORT_DISTANCE.len(); i++)
{
local min=TRANSPORT_DISTANCE[i];
local lim=TRANSPORT_DISTANCE[i+1];
local max=TRANSPORT_DISTANCE[i+2];
if (target == i)
{
if (get_min) { toret=min; }
else { toret=(limited) ? lim : max; }
}
if (min < small) { small=min; }
if (lim > big) { big=lim; }
i+=2; // next iter
}
distanceLimits[0]=small;
distanceLimits[1]=big;
return toret;
}
function cJobs::GetTransportList(distance)
// Return a list of transport we can use
{
// road assign as 2, trains assign as 1, air assign as 4, boat assign as 3
// it's just AIVehicle.VehicleType+1
local v_train=1;
local v_boat =1;
local v_air =1;
local v_road =1;
local tweaklist=AIList();
local road_maxdistance=cJobs.GetTransportDistance(RouteType.ROAD,false,false);
local road_mindistance=cJobs.GetTransportDistance(RouteType.ROAD,true,false);
local rail_maxdistance=cJobs.GetTransportDistance(RouteType.RAIL,false,false);
local rail_mindistance=cJobs.GetTransportDistance(RouteType.RAIL,true,false);
local air_maxdistance=cJobs.GetTransportDistance(RouteType.AIR,false,false);
local air_mindistance=cJobs.GetTransportDistance(RouteType.AIR,true,false);
local water_maxdistance=cJobs.GetTransportDistance(RouteType.WATER,false,false);
local water_mindistance=cJobs.GetTransportDistance(RouteType.WATER,true,false);
//DInfo("Distances: Truck="+road_mindistance+"/"+road_maxdistance+" Aircraft="+air_mindistance+"/"+air_maxdistance+" Train="+rail_mindistance+"/"+rail_maxdistance+" Boat="+water_mindistance+"/"+water_maxdistance,2);
local goal=distance;
if (goal >= road_mindistance && goal <= road_maxdistance) { tweaklist.AddItem(RouteType.ROAD,2*v_road); }
if (goal >= rail_mindistance && goal <= rail_maxdistance) { tweaklist.AddItem(RouteType.RAIL,1*v_train); }
if (goal >= air_mindistance && goal <= air_maxdistance) { tweaklist.AddItem(RouteType.AIR,4*v_air); }
if (goal >= water_mindistance && goal <= water_maxdistance) { tweaklist.AddItem(RouteType.WATER,3*v_boat); }
tweaklist.RemoveValue(0);
return tweaklist;
}
function cJobs::IsTransportTypeEnable(transport_type)
// return true if that transport type is enable in the game
{
switch (transport_type)
{
case RouteType.ROAD:
return (INSTANCE.use_road && INSTANCE.job_road);
case RouteType.AIR:
return (INSTANCE.use_air && INSTANCE.job_air);
case RouteType.RAIL:
return (INSTANCE.use_train && INSTANCE.job_train);
case RouteType.WATER:
return (INSTANCE.use_boat && INSTANCE.job_boat);
}
}
function cJobs::JobIsNotDoable(uid)
// set the undoable status for that job
{
local badjob=cJobs.Load(uid);
if (!badjob) { return; }
badjob.isdoable=false;
cJobs.badJobs.AddItem(uid,0);
}
function cJobs::UpdateDoableJobs()
// Update the doable status of the job indexer
{
INSTANCE.main.jobs.CheckLimitedStatus();
DInfo("Analysing the task pool",0);
local parentListID=AIList();
INSTANCE.main.jobs.jobDoable.Clear();
local topair=0;
local toproad=0;
local toprail=0;
local topwater=0;
cJobs.CostTopJobs[RouteType.RAIL]=0;
cJobs.CostTopJobs[RouteType.AIR]=0;
cJobs.CostTopJobs[RouteType.WATER]=0;
cJobs.CostTopJobs[RouteType.ROAD]=0;
// reset all top jobs
local top50 = 0;
foreach (id, value in cRoute.RouteIndexer)
{
local j = cJobs.Load(id);
if (!j) { continue; }
parentListID.AddItem(j.parentID,0);
}
foreach (id, value in INSTANCE.main.jobs.jobIndexer)
{
if (id == 0 || id == 1) { continue; } // ignore virtual
local doable=1;
local myjob=cJobs.Load(id);
if (!myjob) { continue; }
doable = myjob.isdoable;
// not doable if not doable
local vehtest=null;
if (doable) { doable = cJobs.IsTransportTypeEnable(myjob.roadType); }
// not doable if disabled
if (doable && myjob.ranking==0) { doable=false; }
// not doable if ranking is at 0
//if (doable && (myjob.sourceObject.ScoreRating == 0 || myjob.targetObject.ScoreRating ==0)) { doable = false; }
// not doable if score rating is at 0
if (doable)
// not doable if max distance is limited and lower the job distance
{
local curmax = INSTANCE.main.jobs.GetTransportDistance(myjob.roadType, false, !INSTANCE.main.bank.unleash_road);
if (curmax < myjob.distance) { doable=false; }
}
// not doable if any parent is already in use
if (doable && parentListID.HasItem(myjob.parentID))
{
DInfo("Job already done by parent job ! First pass filter",4);
doable=false;
}
if (doable && !myjob.sourceObject.IsTown && !AIIndustry.IsValidIndustry(myjob.sourceObject.ID)) { doable=false; }
// not doable if the industry no longer exist
if (doable && myjob.roadType == RouteType.AIR && (myjob.sourceObject.CargoProduce.GetValue(cCargo.GetPassengerCargo()) < 100 || myjob.targetObject.CargoProduce.GetValue(cCargo.GetPassengerCargo()) < 100)) { doable=false; }
// not doable because aircraft with poor towns don't make good jobs
if (doable && !INSTANCE.main.bank.unleash_road && myjob.roadType == RouteType.RAIL && myjob.cargoID == cCargo.GetPassengerCargo()) { doable=false; }
// not doable until roads are unleash, trains aren't nice in town, so wait at least a nice big town to build them
if (doable && myjob.sourceObject.IsTown && DictatorAI.GetSetting("allowedjob") == 1) { doable=false; }
// not doable if town jobs is not allow
if (doable && !myjob.sourceObject.IsTown && DictatorAI.GetSetting("allowedjob") == 2) { doable=false; }
// not doable if industry jobs is not allow
if (doable && INSTANCE.safeStart > 0 && myjob.roadType != RouteType.ROAD && cJobs.IsTransportTypeEnable(RouteType.ROAD)) { doable = false; }
// disable until safeStart is over
if (doable)
{
switch (myjob.roadType)
{
case RouteType.AIR:
if (topair < myjob.ranking && myjob.cargoID == cCargo.GetPassengerCargo())
{
cJobs.CostTopJobs[myjob.roadType]=myjob.moneyToBuild;
topair=myjob.ranking;
}
break;
case RouteType.ROAD:
if (toproad < myjob.ranking)
{
cJobs.CostTopJobs[myjob.roadType]=myjob.moneyToBuild;
toproad=myjob.ranking;
}
break;
case RouteType.WATER:
if (topwater < myjob.ranking)
{
cJobs.CostTopJobs[myjob.roadType]=myjob.moneyToBuild;
topwater=myjob.ranking;
}
break;
case RouteType.RAIL:
if (toprail < myjob.ranking)
{
cJobs.CostTopJobs[myjob.roadType]=myjob.moneyToBuild;
toprail=myjob.ranking;
}
break;
}
}
local airValid=(doable && !INSTANCE.main.bank.unleash_road && cJobs.CostTopJobs[RouteType.AIR] > 0 && (cBanker.CanBuyThat(cJobs.CostTopJobs[RouteType.AIR]) || INSTANCE.main.carrier.warTreasure > cJobs.CostTopJobs[RouteType.AIR]) && cJobs.IsTransportTypeEnable(RouteType.AIR) && INSTANCE.safeStart == 0);
if (airValid && myjob.roadType == RouteType.ROAD && myjob.cargoID == cCargo.GetPassengerCargo()) { doable = false; }
// disable because we have funds to build an aircraft job
local trainValid=(doable && !INSTANCE.main.bank.unleash_road && cJobs.CostTopJobs[RouteType.RAIL] > 0 && (cBanker.CanBuyThat(cJobs.CostTopJobs[RouteType.RAIL]) || INSTANCE.main.carrier.warTreasure > cJobs.CostTopJobs[RouteType.RAIL]) && cJobs.IsTransportTypeEnable(RouteType.RAIL) && INSTANCE.safeStart == 0);
if (trainValid && myjob.roadType == RouteType.ROAD) { doable = false; }
// disable if we can make a train instead of a road
if (doable && !cBanker.CanBuyThat(myjob.moneyToBuild)) { doable=false; }
// disable as we lack money
if (doable) { myjob.jobDoable.AddItem(id, myjob.ranking); top50++; }
}
INSTANCE.main.jobs.jobDoable.Sort(AIList.SORT_BY_VALUE, false);
DInfo(INSTANCE.main.jobs.jobIndexer.Count()+" jobs found",2);
DInfo(INSTANCE.main.jobs.jobDoable.Count()+" jobs doable",2);
}
function cJobs::GetJobTarget(src_id, cargo_id, src_istown, srcloc)
// return an AIList with all possibles destinations, return values are manhattan distance from srcloc
{
local retList=AIList();
local rmax=cJobs.GetTransportDistance(0,false,false); // just to make sure min&max are init
if (cCargo.IsCargoForTown(cargo_id))
{
retList=AITownList();
retList.Valuate(AITown.GetPopulation);
retList.Sort(AIList.SORT_BY_VALUE,false);
retList.Valuate(AITown.GetDistanceManhattanToTile, srcloc);
retList.KeepBetweenValue(distanceLimits[0], rmax);
}
else {
retList=AIIndustryList_CargoAccepting(cargo_id);
retList.Valuate(AIIndustry.GetDistanceManhattanToTile, srcloc);
retList.KeepBetweenValue(distanceLimits[0], rmax);
}
return retList;
}
function cJobs::CreateNewJob(srcUID, dstID, cargo_id, road_type, _distance)
// Create a new Job
{
local newjob=cJobs();
newjob.sourceObject = cProcess.Load(srcUID);
if (!newjob.sourceObject) { return; }
if (cCargo.IsCargoForTown(cargo_id)) { dstID=cProcess.GetUID(dstID, true); }
newjob.targetObject = cProcess.Load(dstID);
if (!newjob.targetObject) { return; }
// filters unwanted jobs
if (road_type == RouteType.WATER && (!newjob.sourceObject.WaterAccess || !newjob.targetObject.WaterAccess)) { return; }
// disable boat job without reachable access
if (road_type == RouteType.AIR && cargo_id != cCargo.GetPassengerCargo()) { return; }
// only pass for aircraft, we will randomize if pass or mail later
if (AIIndustry.IsBuiltOnWater(newjob.sourceObject.ID))
{
if (cargo_id == cCargo.GetPassengerCargo()) { if (road_type != RouteType.AIR) { return; } }
else { if (road_type != RouteType.WATER) { return; } }
}
// allow passenger only for aircraft, and other cargos only for boats
if (cargo_id == cCargo.GetPassengerCargo() && !newjob.sourceObject.IsTown && road_type == RouteType.AIR && !AIIndustry.HasHeliport(newjob.sourceObject.ID)) { return; }
// make sure the industry have an heliport we could use for aircraft (choppers), should fix FIRS Industry hotels.
newjob.distance = _distance;
newjob.roadType = road_type;
newjob.cargoID = cargo_id;
newjob.GetUID();
newjob.Save();
INSTANCE.main.jobs.RefreshValue(newjob.UID,true); // update ranking, cargo amount... must be call after GetUID
}
function cJobs::AddNewIndustryOrTown(industryID, istown)
// Add a new industry/town job: this will add all possibles jobs doable with it (transport type + all cargos)
{
local p_uid = cProcess.GetUID(industryID, istown);
local p = cProcess.Load(p_uid);
if (!p) { return; }
local position=p.Location;
local cargoList=p.CargoProduce;
cargoList.RemoveItem(cCargo.GetMailCargo());
// Remove the mail cargo, it's too poor for anyone except trucks, but this add more trucks/bus to town that are already too crowd.
foreach (cargoid, amount in cargoList)
{
local targetList=cJobs.GetJobTarget(p.ID, cargoid, p.IsTown, p.Location); // find where we could transport it
local pause = cLooper();
foreach (destination, distance in targetList)
{
local transportList=GetTransportList(distance); // find possible ways to transport that
local pause = cLooper();
foreach (transtype, dummy2 in transportList)
{
cJobs.CreateNewJob(p.UID, destination, cargoid, transtype, distance);
local pause = cLooper();
}
}
}
}
function cJobs::DeleteJob(uid)
// Remove all job references
{
DInfo("Removing job #"+uid+" from database",4);
if (uid in cJobs.database) { delete cJobs.database[uid]; }
if (cJobs.jobIndexer.HasItem(uid)) { cJobs.jobIndexer.RemoveItem(uid); }
if (cJobs.jobDoable.HasItem(uid)) { cJobs.jobDoable.RemoveItem(uid); }
if (cJobs.badJobs.HasItem(uid)) { cJobs.badJobs.RemoveItem(uid); }
}
function cJobs::DeleteIndustry()
// Remove an industry and all jobs using it
{
if (cJobs.deadIndustry.IsEmpty()) { return; }
foreach (object in cJobs.database)
{
foreach (industryID, _ in cJobs.deadIndustry)
{
if ((!object.sourceObject.IsTown && object.sourceObject.ID == industryID) || (!object.targetObject.IsTown && object.targetObject.ID == industryID)) { cJobs.DeleteJob(object.UID); }
}
local pause = cLooper();
}
cJobs.deadIndustry.Clear();
}
function cJobs::MarkIndustryDead(industry_id)
{
cProcess.DeleteProcess(industry_id);
if (cJobs.rawJobs.HasItem(industry_id)) { cJobs.RawJob_Delete(industry_id); }
else { cJobs.deadIndustry.AddItem(industry_id,0); }
}
function cJobs::RawJobHandling()
// Find a raw Job and add possible jobs from it to jobs database
{
if (cJobs.rawJobs.IsEmpty()) { return; }
local looper = (4 - cRoute.RouteIndexer.Count());
if (looper < 1) { looper=1; }
for (local j=0; j < looper; j++)
{
local p = cProcess.Load(cJobs.rawJobs.Begin());
cJobs.AddNewIndustryOrTown(p.ID, p.IsTown);
cJobs.RawJob_Delete(p.UID);
if (cJobs.rawJobs.IsEmpty()) { break; }
}
if (cJobs.rawJobs.IsEmpty()) { DInfo("All raw jobs have been process",2); }
else { DInfo("rawJobs still to do: "+cJobs.rawJobs.Count(),1); }
}
function cJobs::RawJob_Delete(pUID)
// Remove process from rawJob list
{
if (cJobs.rawJobs.HasItem(pUID))
{
DInfo("Removing industry #"+pUID+" from raw job",4); cJobs.rawJobs.RemoveItem(pUID);
}
}
function cJobs::RawJob_Add(pUID)
// Add a process to rawJob list
{
local p = cProcess.Load(pUID);
if (!p) { return; }
cJobs.rawJobs.AddItem(p.UID, p.Score);
}
function cJobs::PopulateJobs()
// Find towns and industries and add any jobs we could do with them
{
local cargoList=AICargoList();
local alljob=AIList();
DInfo("Finding all industries & towns jobs...",0);
foreach (cargoid, _ in cargoList)
{
local prod=cProcess.GetProcessList_ProducingCargo(cargoid);
DInfo("Production of "+cCargo.GetCargoLabel(cargoid)+" : "+prod.Count(),4);
alljob.AddList(prod);
}
local curr=0;
foreach (puid, _ in alljob)
{
cJobs.RawJob_Add(puid);
curr++;
if (curr % 18 == 0)
{
DInfo(curr+" / "+alljob.Count(),0);
}
local pause = cLooper();
}
cJobs.rawJobs.Sort(AIList.SORT_BY_VALUE, false);
}
function cJobs::CheckTownStatue()
// check if can add a statue to the town
{
if (INSTANCE.fairlevel==0) { return; } // no action if we play easy
DInfo(cProcess.statueTown.Count()+" towns to build statue found.",2);
local temp = AIList();
temp.AddList(cProcess.statueTown);
foreach (townID, dummy in temp)
{
if (AITown.IsActionAvailable(townID, AITown.TOWN_ACTION_BUILD_STATUE))
{
if (AITown.HasStatue(townID)) { cProcess.statueTown.RemoveItem(townID); continue; }
AITown.PerformTownAction(townID, AITown.TOWN_ACTION_BUILD_STATUE);
if (AITown.HasStatue(townID))
{
DInfo("Built a statue at "+AITown.GetName(townID),0);
cProcess.statueTown.RemoveItem(townID);
}
}
local zzz = cLooper();
}
}
function cJobs::GetUIDFromSubsidy(subID, onoff)
// set to onoff the state of subsidy in a job matching the subID subsidy
{
if (!AISubsidy.IsValidSubsidy(subID)) { return; }
local cargoID = AISubsidy.GetCargoType(subID);
local sourceIsTown = (AISubsidy.GetSourceType(subID) == AISubsidy.SPT_TOWN);
local targetIsTown = (AISubsidy.GetDestinationType(subID) == AISubsidy.SPT_TOWN);
local sourceID = AISubsidy.GetSourceIndex(subID);
local targetID = AISubsidy.GetDestinationIndex(subID);
foreach (UID, _dummy in cJobs.jobDoable)
{
local j = cJobs.Load(UID);
if (j.cargoID == cargoID && j.sourceObject.IsTown == sourceIsTown && j.targetObject.IsTown == targetIsTown && j.sourceObject.ID == sourceID && j.targetObject.ID == targetID)
{
j.subsidy=onoff;
DInfo("Setting subsidy to "+onoff+" for jobs "+j.Name,1);
return;
}
}
}
function cJobs::SubsidyOn(subID)
{
cJobs.GetUIDFromSubsidy(subID, true);
}
function cJobs::SubsidyOff(subID)
{
cJobs.GetUIDFromSubsidy(subID, false);
}
DictatorAI/handler/pathfinder.nut 0000644 0001750 0000144 00000022246 12202775160 016402 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cPathfinder extends cClass
// Use it with cPathfinder.GetStatus function to create and manage tasks
// Return state is -1= error, 2=success, so while -1 or 2 keep querying GetStatus
{
static database = {};
static function GetPathfinderObject(UID)
{
if (UID in cPathfinder.database) { return cPathfinder.database[UID]; }
else { return null; }
}
UID = null; // UID of the pathfinding task
signHandler = null; // the sign ID that handle this task
pathHandler = null; // the pathfinder instance
solve = null; // the solve array
timer = null; // the time we spend on that task
source = null; // the source [] to pathfind
target = null; // the target [] to pathfind
status = null; // 0 - working, -1 fail, 1 success pathfind, 2 success route build, 3 wait for child to finish
stationID = null; // the stationID rails will be assign to
useEntry = null; // the entry of the stationID will be use or not
// if we success at pathfinding, we will try build the route, and if we fail at building the route, we will recall the pathfinder or try to rebuild it
// we will then report failure in status
r_source = null; // the original pathfind source point, use when pathfinding a subtask.
r_target = null; // the original pathfind target point
constructor()
{
// * are saved variables
UID = null;
signHandler = 0;
pathHandler = null;
solve = null;
timer = 0;
source = [];
target = [];
stationID = null;
useEntry = null;
status = 0;
r_source = null;
r_target = null;
this.ClassName = "cPathfinder";
}
}
// public
function cPathfinder::GetUID(src, tgt)
// return UID of the task
{
src = cPathfinder.GetSourceX(src);
tgt = cPathfinder.GetTargetX(tgt);
local ss = typeof(src);
local ts = typeof(tgt);
if (ss != "array" || ts != "array") { DInfo("Bad pathfinder source ("+ss+") or target ("+ts+")",1); return null; }
if (src.len() != 2 || tgt.len() != 2) { DInfo("Bad pathfinder source ("+src.len()+") or target ("+tgt.len()+")",1); return null; }
if (!AIMap.IsValidTile(src[0]) || !AIMap.IsValidTile(tgt[1])) { return null; }
return src[0]+tgt[1];
}
function cPathfinder::GetStatus(source, target, stationID, useEntry = null)
// return the status of the task, and create it if we didn't plane it yet
{
source = cPathfinder.GetSourceX(source);
target = cPathfinder.GetTargetX(target);
local uid=cPathfinder.GetUID(source, target);
if (uid == null) { DError("Invalid pathfinder task : "+source[0]+" / "+source[1]+" / "+target[0]+" / "+target[1],1); return -1; }
if (uid in cPathfinder.database) { }
else { cPathfinder.CreateNewTask(source, target, useEntry, stationID); return 0; }
local pathstatus=cPathfinder.GetPathfinderObject(uid);
return pathstatus.status;
}
function cPathfinder::AdvanceAllTasks()
// Advance all tasks handle by the pathfinder, if openttd handle multi-core/cpu this would be a huge help here
{
foreach (task in cPathfinder.database) cPathfinder.AdvanceTask(task.UID);
}
function cPathfinder::GetSolve(source, target)
// return the solver instance
{
source = cPathfinder.GetSourceX(source);
target = cPathfinder.GetTargetX(target);
local UID=cPathfinder.GetUID(source, target);
if (UID == null) { DError("Invalid pathfinder task : "+source[0]+" / "+source[1]+" / "+target[0]+" / "+target[1],1); return -1; }
local pftask=cPathfinder.GetPathfinderObject(UID);
return pftask.solve;
}
function cPathfinder::CloseTask(source, target)
// Destroy that task
{
source = cPathfinder.GetSourceX(source);
target = cPathfinder.GetTargetX(target);
local UID=cPathfinder.GetUID(source, target);
if (UID == null) { DError("Invalid pathfinder task : "+source[0]+" / "+source[1]+" / "+target[0]+" / "+target[1],1); return -1; }
local pftask=cPathfinder.GetPathfinderObject(UID);
if (pftask == null) { return; }
if (pftask.UID in cPathfinder.database)
{
delete cPathfinder.database[pftask.UID];
AISign.RemoveSign(pftask.signHandler);
DInfo("Pathfinder task "+pftask.UID+" closed.",1);
}
}
function cPathfinder::AdvanceTask(UID)
// Advance the pathfinding search
{
local maxTimer=300; // maximum time put on pathfinding a path
local maxStep=5; // maximum time put on a try
local _counter=0;
local pftask=cPathfinder.GetPathfinderObject(UID);
switch (pftask.status)
{
case -1:
DInfo("Pathfinder task "+pftask.UID+" has end : nothing more could be done, failure.",1);
return;
case 1:
if (!cBanker.CanBuyThat(30000)) { return; }
cBanker.RaiseFundsBy(30000);
DInfo("Pathfinder task "+pftask.UID+" has end search: trying to build the route found.",1);
if (pftask.useEntry == null) { cBuilder.AsyncConstructRoadROAD(pftask.source[0], pftask.target[1], pftask.stationID); }
else { cBuilder.BuildRoadRAIL(pftask.source, pftask.target, pftask.useEntry, pftask.stationID); }
return;
case 2:
DInfo("Pathfinder task "+pftask.UID+" has end building the path.",1);
return;
case 3:
DInfo("Pathfinder task "+pftask.UID+" is waiting its subtask result.",1);
return;
}
if (pftask.status != 0) { DInfo("Pathfinder task "+pftask.UID+" search is over with status "+pftask.status,1); return; }
local check=false;
DInfo("Pathfinder is working on task "+pftask.UID);
while (check == false && _counter < maxStep)
{
check = pftask.pathHandler.FindPath(maxTimer);
_counter++; pftask.timer++;
pftask.InfoSign("Pathfinding "+pftask.UID+"... "+pftask.timer);
}
if (check != null && check != false)
{
DInfo("Pathfinder found a path for task "+pftask.UID+" @"+pftask.timer,1);
pftask.status=1;
pftask.InfoSign("Pathfinding "+pftask.UID+"... FOUND!");
pftask.solve=check;
return;
}
if (check == null || pftask.timer > maxTimer)
{
DInfo("Pathfinder task "+pftask.UID+" failure",1);
pftask.InfoSign("Pathfinding "+pftask.UID+"... failure");
pftask.status=-1;
}
if (!AIStation.IsValidStation(pftask.stationID))
{
DInfo("Pathfinder is autoclosing task "+pftask.UID,1);
cPathfinder.CloseTask(pftask.source[0], pftask.target[1]);
}
}
// private
function cPathfinder::GetSourceX(x)
// This convert integer coord to internal usage (same as railpathfinder)
{
if (typeof(x) == "integer") { return [x,0]; }
return x;
}
function cPathfinder::GetTargetX(x)
// This convert integer coord to internal usage (same as railpathfinder)
{
if (typeof(x) == "integer") { return [0, x]; }
return x;
}
function cPathfinder::InfoSign(msg)
// Update the sign and recreate it if need
{
local loc=-1;
if (AISign.IsValidSign(this.signHandler)) { loc=AISign.GetLocation(this.signHandler); }
if (loc != this.target[1]) { loc=-1; }
if (loc != -1) { AISign.SetName(this.signHandler, msg); }
else { this.signHandler=AISign.BuildSign(this.target[1],msg); }
}
function cPathfinder::CreateNewTask(src, tgt, entrance, station)
// Create a new pathfinding task
{
local pftask=cPathfinder();
src = cPathfinder.GetSourceX(src);
tgt = cPathfinder.GetTargetX(tgt);
pftask.UID=cPathfinder.GetUID(src, tgt);
pftask.source=src;
pftask.target=tgt;
pftask.InfoSign("Pathfinder: task #"+cPathfinder.database.len());
pftask.useEntry=entrance;
pftask.stationID=station;
if (entrance == null)
{
// road
pftask.pathHandler= MyRoadPF();
pftask.pathHandler.cost.bridge_per_tile = 90;
pftask.pathHandler.cost.tunnel_per_tile = 90;
pftask.pathHandler.cost.turn = 200;
pftask.pathHandler.cost.max_bridge_length=30;
pftask.pathHandler.cost.max_tunnel_length=30;
pftask.pathHandler.cost.tile=70;
pftask.pathHandler.cost.slope=120;
pftask.pathHandler._cost_level_crossing = 120;
pftask.pathHandler.InitializePath([pftask.source[0]], [pftask.target[1]]);
}
else // rail
{
pftask.pathHandler= MyRailPF();
/*
pftask.pathHandler.cost.bridge_per_tile = 110;//70
pftask.pathHandler.cost.tunnel_per_tile = 110;//70
pftask.pathHandler.cost.turn = 240;//200
pftask.pathHandler.cost.max_bridge_length=30;
pftask.pathHandler.cost.max_tunnel_length=30;
pftask.pathHandler.cost.tile=100;//70
pftask.pathHandler.cost.slope=240;//80
pftask.pathHandler.cost.diagonal_tile=110;
*/
pftask.pathHandler.cost.bridge_per_tile = 190;//70
pftask.pathHandler.cost.tunnel_per_tile = 180;//70
pftask.pathHandler.cost.turn = 200;//200
pftask.pathHandler.cost.max_bridge_length=30;
pftask.pathHandler.cost.max_tunnel_length=30;
pftask.pathHandler.cost.tile=100;//70
pftask.pathHandler.cost.slope=250;//80
pftask.pathHandler.cost.coast = 100;
pftask.pathHandler.cost.diagonal_tile=100;
pftask.pathHandler.InitializePath([pftask.source], [pftask.target]);
}
DInfo("New pathfinder task : "+pftask.UID,1);
cPathfinder.database[pftask.UID] <- pftask;
}
DictatorAI/handler/enginehandler.nut 0000644 0001750 0000144 00000021127 12203206631 017047 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cEngine extends cEngineLib
{
static BestEngineList=AIList(); // list of best engine for a couple engine/cargos, item=EUID, value=best engineID
rabbit = null;
constructor()
{
::cEngineLib.constructor();
this.rabbit = -1; // the rabbit is a vehicle with engine that is going to depot for upgrade, blocking others upgrade
}
}
function cEngine::IsRabbitSet(vehicleID)
// return true if we have a test vehicle already set
{
local engineID = AIVehicle.GetEngineType(vehicleID);
local eng = cEngine.Load(engineID);
if (eng == null) return;
if (!AIVehicle.IsValidVehicle(eng.is_known)) { eng.is_known = -2; }
return (eng.is_known > -1);
}
function cEngine::RabbitSet(vehicleID)
// Set the status of the engine as a rabbit vehicle is on its way for testing
{
if (vehicleID == null) { return ; }
local engineID=AIVehicle.GetEngineType(vehicleID);
if (engineID == null) { return ; }
local eng = cEngine.Load(engineID);
if (eng.is_known == -2) {
eng.is_known = vehicleID;
INSTANCE.DInfo("Using "+AIVehicle.GetName(vehicleID)+" as test vehicle for "+cEngine.GetName(engineID),1);
}
}
function cEngine::RabbitUnset(vehicleID)
// Unset the status of the rabbit vehicle, only useful if the rabbit vehicle never reach a depot (crash)
{
if (vehicleID == null || !AIVehicle.IsValidVehicle(vehicleID)) return ;
local engineID = AIVehicle.GetEngineType(vehicleID);
if (!AIEngine.IsValidEngine(engineID)) return ;
local eng=cEngine.Load(engineID);
if (eng == null) return;
if (eng.is_known >= 0) eng.is_known = -2;
}
function cEngine::CanPullCargo(engineID, cargoID)
// try to really answer if an engine can be use to pull a wagon of a cargo type
// if NicePlay is true we return the AIEngine.CanPullCargo version
// else we return real usable wagons list for a train
{
local setting = !DictatorAI.GetSetting("use_nicetrain");
return cEngineLib.CanPullCargo(engineID, cargoID, setting);
}
function cEngine::GetName(eID)
// return the name of the engine
{
local name = "Invalid engine";
if (AIEngine.IsValidEngine(eID)) name = AIEngine.GetName(eID);
name+=" (#"+eID+")";
return name;
}
function cEngine::GetEUID(engineType, cargoID)
// return the EUID
// engineType : it's AIVehicle.GetEngineType() result for an engine except trains
// engineType : for trains it's RouteType.CHOPPER+2+Railtype value
// cargoID : for road/water/train it's the cargo ID
// cargoID : for aircraft it's the value of RouteType.AIR/AIRNET/CHOPPER
{
engineType++; // no 0 base results
return (engineType*40)+cargoID; // 32 cargos only, so 40 is really enough
}
function cEngine::GetEngineByCache(engineType, cargoID)
// return the top engine if we knows it already
// return -1 if we have no match but try to find an engine before
{
local EUID=cEngine.GetEUID(engineType, cargoID);
if (cEngine.BestEngineList.HasItem(EUID)) return cEngine.BestEngineList.GetValue(EUID);
INSTANCE.DInfo("Engine cache miss for "+EUID,2);
local etype = engineType;
local rtype = -1;
if (etype > RouteType.CHOPPER) { etype = AIVehicle.VT_RAIL; rtype = engineType - RouteType.CHOPPER - 2; }
switch (etype)
{
case AIVehicle.VT_ROAD:
local engine = cCarrier.GetRoadVehicle(null, cargoID);
if (engine != -1) cEngine.SetBestEngine(EUID, engine);
return engine;
case AIVehicle.VT_RAIL:
local engine = cCarrier.ChooseRailCouple(cargoID, rtype);
if (engine[0] != -1) {
cEngine.SetBestEngine(EUID, engine[0]);
if (cJobs.WagonType.HasItem(cargoID)) cJobs.WagonType.RemoveItem(cargoID);
cJobs.WagonType.AddItem(cargoID, engine[1]);
}
return engine[0];
case AIVehicle.VT_AIR:
local engine = cCarrier.GetAirVehicle(null, cargoID);
if (engine != -1) cEngine.SetBestEngine(EUID, engine);
return engine;
}
return -1;
}
function cEngine::SetBestEngine(EUID, engineID)
// set the best engine for that EUID
{
if (EUID==0) return true;
local exist=(cEngine.BestEngineList.HasItem(EUID));
local oldvalue=-1;
if (exist) {
oldvalue=cEngine.BestEngineList.GetValue(EUID);
cEngine.BestEngineList.SetValue(EUID, engineID);
if (oldvalue != engineID) INSTANCE.DInfo("Setting new top engine for EUID #"+EUID+" to "+engineID+"-"+AIEngine.GetName(engineID)+" was "+oldvalue+"-"+AIEngine.GetName(engineID),2);
}
else cEngine.BestEngineList.AddItem(EUID, engineID);
}
function cEngine::RailTypeIsTop(engineID, cargoID, setTopRail)
// Check if we could use another train with a better engine by changing railtype
// setTopRail : true to set it, false to only grab the value
// return -1 if we are at top already
// return the engineID if we could upgrade
{
if (AIEngine.GetVehicleType(engineID) != AIVehicle.VT_RAIL) return -1;
local EUID = cEngine.GetEUID(RouteType.RAIL, cargoID);
local topengine = engineID;
if (!cEngine.BestEngineList.HasItem(EUID)) setTopRail = true;
if (setTopRail) cEngine.SetBestEngine(EUID, engineID);
topengine = cEngine.BestEngineList.GetValue(EUID);
local toprail = cEngineLib.GetBestRailType(topengine);
local engrail = cEngineLib.GetBestRailType(engineID);
if (toprail == engrail) { return -1; }
else {
print("BREAKRAIL : New best railtype for "+cCargo.GetCargoLabel(cargoID)+" set to use "+cEngine.GetName(topengine)+" using railtype "+AIRail.GetName(cEngineLib.GetBestRailType(topengine)));
return toprail; // we return the railtype need to upgrade
}
}
function cEngine::EngineIsTop(engineID, cargoID, setTopEngine)
// Check if we can use a better engine for a vehicle
// engineID: the engine ID we wish to test for an upgrade
// cargoID: for water/road/rail the cargo ID
// cargoID: for aircraft RouteType.AIR/AIRNET/CHOPPER
// setTopEngine: true to set it, false to only grab the value
// return -1 if we are at top engine already
// return engineID if we can upgrade to a better version
{
//if (cargoID == -1) return -1;
local vehicleType = AIEngine.GetVehicleType(engineID);
local special = null;
if (vehicleType == AIVehicle.VT_RAIL)
{
local RT = AIEngine.GetRailType(engineID);
special = RT+RouteType.CHOPPER+2;
}
else special=vehicleType;
local EUID=cEngine.GetEUID(special, cargoID);
local topengine=engineID;
if (EUID==0) return -1; // on error say we're at top
if (!cEngine.BestEngineList.HasItem(EUID)) setTopEngine=true;
if (setTopEngine) cEngine.SetBestEngine(EUID, engineID);
topengine=cEngine.BestEngineList.GetValue(EUID);
if (engineID == topengine) return -1;
else {
INSTANCE.DInfo("Engine "+AIEngine.GetName(engineID)+" can be upgrade for engine "+AIEngine.GetName(topengine),2);
return topengine;
}
}
function cEngine::IsVehicleAtTop(vehID)
// Check if a vehicle is using the best engine already
// return -1 if the vehicle doesn't need upgrade
// return the better engineID if one exist
{
if (!AIVehicle.IsValidVehicle(vehID)) { INSTANCE.DError("Not a valid vehicle",2); return -1; }
local idx=cCarrier.VehicleFindRouteIndex(vehID);
if (idx == null) { INSTANCE.DError("Fail to find the route used by this vehicle: "+cCarrier.GetVehicleName(vehID),2); return -1; }
local road=cRoute.Load(idx);
if (!road) return -1;
local cargoID=road.CargoID;
local vehType=AIVehicle.GetVehicleType(vehID);
if (vehType==AIVehicle.VT_AIR) cargoID=road.VehicleType;
local engineID=AIVehicle.GetEngineType(vehID);
return cEngine.EngineIsTop(engineID, cargoID, false);
}
function cEngine::CheckMaxSpeed(engineID)
// Check the max speed of vehicle and see if top speed for that vehicle type should be this one or not
{
local topspeed = cEngine.GetMaxSpeed(engineID);
local typeveh = cEngine.GetVehicleType(engineID);
switch (typeveh)
{
case AIVehicle.VT_RAIL:
if (INSTANCE.main.carrier.speed_MaxTrain < topspeed)
{
INSTANCE.DInfo("Setting maximum speed for trains vehicle to "+topspeed,0);
INSTANCE.main.carrier.speed_MaxTrain = topspeed;
}
return;
case AIVehicle.VT_ROAD:
if (INSTANCE.main.carrier.speed_MaxRoad < topspeed)
{
INSTANCE.DInfo("Setting maximum speed for roads vehicle to "+topspeed,0);
INSTANCE.main.carrier.speed_MaxRoad = topspeed;
}
return;
}
}
DictatorAI/handler/routes.nut 0000644 0001750 0000144 00000040502 12202252267 015570 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cRoute::RouteAirportCheck(uid=null)
// this function check airports routes and setup some properties as they should be
{
local road=false;
if (uid == null) road=this;
else road=cRoute.Load(uid);
if (!road || road.VehicleType < RouteType.AIR) return;
local oldtype = road.VehicleType;
road.VehicleType = RouteType.AIR;
if (road.UID < 2) road.VehicleType = RouteType.AIRNET;
if (road.CargoID == cCargo.GetMailCargo()) road.VehicleType++;
local srcValid = (typeof(road.SourceStation) == "instance");
local dstValid = (typeof(road.TargetStation) == "instance");
if (road.UID > 1 && srcValid && dstValid && (!cBuilder.AirportAcceptBigPlanes(road.SourceStation.s_ID) || !cBuilder.AirportAcceptBigPlanes(road.TargetStation.s_ID))) road.VehicleType+=4;
// adding 4 to met small AIR or MAIL
if (!road.SourceProcess.IsTown) road.VehicleType = RouteType.CHOPPER;
if (oldtype != road.VehicleType) { DInfo("Changing aircrafts type for route "+road.Name+" to "+cRoute.RouteTypeToString(road.VehicleType),1); road.SetRouteName(); }
}
function cRoute::RouteUpdateVehicle()
// Recount vehicle at stations & route, update route stations
{
if (this.UID < 2)
{
local maillist=AIVehicleList_Group(this.GetVirtualAirMailGroup());
local passlist=AIVehicleList_Group(this.GetVirtualAirPassengerGroup());
this.VehicleCount=maillist.Count()+passlist.Count();
return;
}
if (!this.Status == RouteStatus.WORKING) return;
this.SourceStation.s_VehicleCount = AIVehicleList_Station(this.SourceStation.s_ID).Count();
this.SourceStation.UpdateCapacity();
this.TargetStation.s_VehicleCount = AIVehicleList_Station(this.TargetStation.s_ID).Count();
this.TargetStation.UpdateCapacity();
local vehingroup = null;
if (this.GroupID == null) vehingroup = 0;
else vehingroup = AIVehicleList_Group(this.GroupID);
this.VehicleCount=vehingroup.Count();
}
function cRoute::SetRouteGroupName(groupID, r_source, r_target, r_stown, r_ttown, r_cargo, isVirtual, sourceStaID, targetStaID)
// This rename a group to a format we can read
{
if (groupID == null || !AIGroup.IsValidGroup(groupID)) return "invalid";
local dummychar="A";
local dummycount=65; // the ASCII A, as this is also A in unicode
local st="I";
if (r_stown) st="T";
local dt="I";
if (r_ttown) dt="T";
if (r_source==null) r_source="B";
if (r_target==null) r_target="B";
local endname="*"+r_cargo+"*"+st+r_source+"*"+dt+r_target+"*"+sourceStaID+"*"+targetStaID+"*0"; // *0 reserved for saving purpose
if (isVirtual) endname="-NETWORK "+AICargo.GetCargoLabel(r_cargo);
dummychar=dummycount.tochar();
local groupname=dummychar+endname;
while (!AIGroup.SetName(groupID, groupname))
{
dummycount++;
dummychar=dummycount.tochar();
groupname=dummychar+endname;
}
}
function cRoute::Route_GroupNameSave()
// This update groupname with new state of the 4 properites we save in its name
// "this" must be an instance of cRoute
{
local newstate = this instanceof cRoute;
if (!newstate) { DError("must be called by an instance of cRoute",1); return false; }
if (this.GroupID == null || !AIGroup.IsValidGroup(this.GroupID)) return false;
newstate = 0;
newstate = this.Source_RailEntry ? newstate=cMisc.SetBit(newstate, 0) : newstate=cMisc.ClearBit(newstate, 0);
newstate = this.Target_RailEntry ? newstate=cMisc.SetBit(newstate, 1) : newstate=cMisc.ClearBit(newstate, 1);
newstate = this.Primary_RailLink ? newstate=cMisc.SetBit(newstate, 2) : newstate=cMisc.ClearBit(newstate, 2);
newstate = this.Secondary_RailLink ? newstate=cMisc.SetBit(newstate, 3) : newstate=cMisc.ClearBit(newstate, 3);
local gname = AIGroup.GetName(GroupID);
gname = gname.slice(0, gname.len()-1) + newstate; // replace last char
return AIGroup.SetName(this.GroupID, gname);
}
function cRoute::RouteBuildGroup()
// Build a group for that route
{
local rtype=this.VehicleType;
if (rtype >= RouteType.AIR) rtype=RouteType.AIR;
local gid = AIGroup.CreateGroup(rtype);
if (!AIGroup.IsValidGroup(gid)) { DError("Cannot create the group, this is serious error, please report it!",0); return; }
this.GroupID = gid;
cRoute.SetRouteGroupName(this.GroupID, this.SourceProcess.ID, this.TargetProcess.ID, this.SourceProcess.IsTown, this.TargetProcess.IsTown, this.CargoID, false, this.SourceStation.s_ID, this.TargetStation.s_ID);
if (this.GroupID in cRoute.GroupIndexer) cRoute.GroupIndexer.SetValue(this.GroupID, this.UID);
else cRoute.GroupIndexer.AddItem(this.GroupID, this.UID);
}
function cRoute::CreateNewRoute(UID)
// Create and add to database a new route with informations taken from cJobs
{
local jobs=cJobs.Load(UID);
if (!jobs) return; // workaround to loading savegame where the jobs has disapears
jobs.isUse = true;
this.UID = jobs.UID;
this.SourceProcess = jobs.sourceObject;
this.TargetProcess = jobs.targetObject;
this.VehicleType = jobs.roadType;
this.CargoID = jobs.cargoID;
switch (this.VehicleType)
{
case RouteType.RAIL:
this.StationType=AIStation.STATION_TRAIN;
break;
case RouteType.ROAD:
this.StationType=AIStation.STATION_TRUCK_STOP;
if (this.CargoID == cCargo.GetPassengerCargo()) this.StationType=AIStation.STATION_BUS_STOP;
break;
case RouteType.WATER:
this.StationType=AIStation.STATION_DOCK;
break;
case RouteType.AIR:
this.StationType=AIStation.STATION_AIRPORT;
local randcargo=AIBase.RandRange(100);
if (randcargo >60) { this.CargoID=cCargo.GetMailCargo(); this.VehicleType=RouteType.SMALLMAIL; }
else { this.CargoID=cCargo.GetPassengerCargo(); this.VehicleType=RouteType.SMALLAIR; }
if (!this.SourceProcess.IsTown) { this.CargoID=cCargo.GetPassengerCargo(); this.VehicleType=RouteType.CHOPPER; }
DInfo("Airport work, choosen : "+randcargo+" "+cCargo.GetCargoLabel(this.CargoID),1);
break;
}
this.Status = 0;
this.RouteSetDistance();
this.RouteSave();
}
function cRoute::RouteRebuildIndex()
// Rebuild our routes index from our datase
{
cRoute.RouteIndexer.Clear();
foreach (item in cRoute.database)
{
cRoute.RouteIndexer.AddItem(item.UID, 1);
if (item.GroupID in cRoute.GroupIndexer) cRoute.GroupIndexer.SetValue(item.GroupID, item.UID);
else if (item.GroupID != null) cRoute.GroupIndexer.AddItem(item.GroupID, item.UID);
}
}
function cRoute::InRemoveList(uid)
// Add a route to route damage with dead status so it will get clear
{
local road = cRoute.GetRouteObject(uid);
if (cRoute.RouteDamage.HasItem(uid)) cRoute.RouteDamage.RemoveItem(uid);
if (road == null) { return; }
cRoute.RouteDamage.AddItem(uid, RouteStatus.DEAD);
}
function cRoute::RouteIsNotDoable()
// When a route is dead, we remove it this way, in 2 steps, next step is RouteUndoableFreeOfVehicle()
{
if (this.UID < 2) return; // don't touch virtual routes
DInfo("Marking route "+cRoute.GetRouteName(this.UID)+" undoable !!!",1);
cJobs.JobIsNotDoable(this.UID);
this.Status = RouteStatus.DEAD;
cRoute.InRemoveList(this.UID);
}
function cRoute::RouteRailGetPathfindingLine(uid, mainline)
// return [] of pathfinding value of mainline or alternate line
{
local road = cRoute.GetRouteObject(uid); // can't use Load() to not get caught by the patrol
if (typeof(road) != "instance") return -1;
if (typeof(road.SourceStation) != "instance") return -1;
if (typeof(road.TargetStation) != "instance") return -1;
local path = [];
local srclink, dstlink, srcpos, dstpos;
if (mainline)
{
if (road.Source_RailEntry) srclink=road.SourceStation.s_EntrySide[TrainSide.IN_LINK];
else srclink=road.SourceStation.s_ExitSide[TrainSide.IN_LINK];
if (road.Target_RailEntry) dstlink=road.TargetStation.s_EntrySide[TrainSide.OUT_LINK];
else dstlink=road.TargetStation.s_ExitSide[TrainSide.OUT_LINK];
}
else {
if (road.Source_RailEntry) srclink=road.SourceStation.s_EntrySide[TrainSide.OUT_LINK];
else srclink=road.SourceStation.s_ExitSide[TrainSide.OUT_LINK];
if (road.Target_RailEntry) dstlink=road.TargetStation.s_EntrySide[TrainSide.IN_LINK];
else dstlink=road.TargetStation.s_ExitSide[TrainSide.IN_LINK];
}
srcpos = srclink+cStationRail.GetRelativeTileBackward(road.SourceStation.s_ID, road.Source_RailEntry);
dstpos = dstlink+cStationRail.GetRelativeTileBackward(road.TargetStation.s_ID, road.Target_RailEntry);
return [srclink, srcpos, dstlink, dstpos];
}
function cRoute::RouteUndoableFreeOfVehicle(uid)
// This is the last step of marking a route undoable
{
if (uid < 2) return; // don't touch virtuals
local route = cRoute.GetRouteObject(uid); // the Load function will return false has route is mark DEAD
if (route != null)
{
local vehlist = AIList();
if (route.GroupID != null && AIGroup.IsValidGroup(route.GroupID))
{
vehlist = AIVehicleList_Group(route.GroupID);
vehlist.Valuate(AIVehicle.GetState);
vehlist.KeepValue(AIVehicle.VS_IN_DEPOT);
foreach (veh, _ in vehlist) INSTANCE.main.carrier.VehicleSell(veh, false);
vehlist = AIVehicleList_Group(route.GroupID);
foreach (veh, _ in vehlist)
{
if (!AIOrder.IsGotoDepotOrder(veh, AIOrder.ResolveOrderPosition(veh, AIOrder.ORDER_CURRENT)))
{
cCarrier.ToDepotList.RemoveItem(veh);
cCarrier.VehicleOrdersReset(veh);
cCarrier.VehicleSendToDepot(veh, DepotAction.REMOVEROUTE);
}
}
}
if (!vehlist.IsEmpty()) return;
local stasrc = null;
local stadst = null;
if (cMisc.ValidInstance(route.SourceStation)) { stasrc = route.SourceStation.s_ID; route.RouteReleaseStation(route.SourceStation.s_ID); }
if (cMisc.ValidInstance(route.TargetStation)) { stadst = route.TargetStation.s_ID; route.RouteReleaseStation(route.TargetStation.s_ID); }
cBuilder.DestroyStation(stasrc);
cBuilder.DestroyStation(stadst);
if (route.GroupID != null) { AIGroup.DeleteGroup(route.GroupID); cRoute.GroupIndexer.RemoveItem(route.GroupID); }
if (route.UID in cRoute.database)
{
DInfo("-> Removing route "+route.UID+" from database",1);
cRoute.RouteIndexer.RemoveItem(route.UID);
delete cRoute.database[route.UID];
}
}
if (cRoute.RouteDamage.HasItem(uid)) cRoute.RouteDamage.RemoveItem(uid);
}
function cRoute::CreateNewStation(start)
// Create a new station for that route at source or destination
// The stationID must be pass thru SourceStation or TargetStation property
// return null on failure, else the new station object created
{
local scheck = null;
if (start) scheck = cStation.InitNewStation(this.SourceStation);
else scheck = cStation.InitNewStation(this.TargetStation);
if (scheck == null) return null;
this.RouteAirportCheck();
return scheck;
}
function cRoute::RouteReleaseStation(stationid)
// Release a station for our route and remove us from its owner list
{
if (stationid == null) return ;
local ss = (cMisc.ValidInstance(this.SourceStation));
local sd = (cMisc.ValidInstance(this.TargetStation));
if (ss && this.SourceStation.s_ID == stationid)
{
local ssta=cStation.Load(this.SourceStation.s_ID);
if (ssta != false) ssta.OwnerReleaseStation(this.UID);
this.SourceStation = null;
this.Status=RouteStatus.DEAD;
}
if (sd && this.TargetStation.s_ID == stationid)
{
local ssta=cStation.Load(this.TargetStation.s_ID);
if (ssta != false) ssta.OwnerReleaseStation(this.UID);
this.TargetStation = null;
this.Status=RouteStatus.DEAD;
}
if (INSTANCE.main.route.RouteDamage.HasItem(this.UID)) INSTANCE.main.route.RouteDamage.RemoveItem(this.UID);
INSTANCE.buildDelay=0; INSTANCE.main.bank.canBuild=true;
}
function cRoute::GetDepot(uid, source=0)
// Return a valid depot we could use, this mean we will seek out both side of the route if we cannot find a proper one
// source: 0- Get any depot we could use, 1- Get source depot, 2- Get target depot
// per default return any valid depot we could found, if source=1 or 2 return an error if the query depot doesn't exist
// return -1 on errors
{
local road=cRoute.Load(uid);
if (!road) return -1;
local sdepot=-1;
local tdepot=-1;
if (typeof(road.SourceStation) == "instance") sdepot=road.SourceStation.s_Depot;
if (typeof(road.TargetStation) == "instance") tdepot=road.TargetStation.s_Depot;
if (road.VehicleType == RouteType.RAIL)
{
local se, sx, de, dx=-1;
if (road.SourceStation instanceof cStationRail)
{
se=road.SourceStation.s_EntrySide[TrainSide.DEPOT];
sx=road.SourceStation.s_ExitSide[TrainSide.DEPOT];
}
if (road.TargetStation instanceof cStation)
{
de=road.TargetStation.s_EntrySide[TrainSide.DEPOT];
dx=road.TargetStation.s_ExitSide[TrainSide.DEPOT];
}
local one, two, three, four=null;
if (road.Source_RailEntry) { one=se; three=sx; }
else { one=sx; three=se; }
if (road.Target_RailEntry) { two=de; four=dx; }
else { two=dx; four=de; }
if (source==0 || source==1)
{
if (cStation.IsDepot(one)) return one;
if (cStation.IsDepot(three)) return three;
}
if (source==0 || source==2)
{
if (cStation.IsDepot(two)) return two;
if (cStation.IsDepot(four)) return four;
}
}
else {
if ((source==0 || source==1) && cStation.IsDepot(sdepot)) return sdepot;
if ((source==0 || source==2) && cStation.IsDepot(tdepot)) return tdepot;
if (road.VehicleType == RouteType.ROAD && road.Status == RouteStatus.WORKING) cBuilder.RouteIsDamage(uid);
if (road.VehicleType == RouteType.WATER && road.Status == RouteStatus.WORKING) cBuilder.RepairWaterRoute(uid);
}
if (source==0) DError("Route "+cRoute.GetRouteName(road.UID)+" doesn't have any valid depot !",2);
else DError("Route "+cRoute.GetRouteName(road.UID)+" doesn't have the request depot ! source="+source,2);
return -1;
}
function cRoute::AddTrain(uid, vehID)
// Add a train to that route, callback cTrain to inform it too
// uid : the route UID
// vehID: the train ID to add
{
local road=cRoute.GetRouteObject(uid);
if (!AIVehicle.IsValidVehicle(vehID)) { DError("Invalid vehicleID: "+vehID,2); return -1; }
if (!road) { DError("Invalid uid : "+uid,2); return -1; }
cTrain.TrainSetStation(vehID, road.SourceStation.s_ID, true, road.Source_RailEntry, true); // train load at station
cTrain.TrainSetStation(vehID, road.TargetStation.s_ID, false, road.Target_RailEntry, road.Twoway); // if twoway train load at station, else it will only drop
// hmmm, choices: a two way route == 2 taker that are also dropper train
// we could then tell stations we have 2 taker == each train will have a platform
// or 2 dropper == station will have just 1 platform and trains must wait on the line
// for now i choose saying they are both taker
road.SourceStation.StationAddTrain(true, road.Source_RailEntry);
road.TargetStation.StationAddTrain(road.Twoway, road.Target_RailEntry);
}
function cRoute::CanAddTrainToStation(uid)
// return true if we can add another train to that rail station
// return false when the station cannot handle it
{
if (!INSTANCE.use_train) return false;
local road=cRoute.GetRouteObject(uid);
if (!road) { DError("Invalid uid : "+uid,2); return -1; }
local canAdd=true;
DInfo("src="+road.Source_RailEntry+" 2way="+road.Twoway+" tgt="+road.Target_RailEntry,1);
canAdd=cBuilder.RailStationGrow(road.SourceStation.s_ID, road.Source_RailEntry, true);
if (canAdd) canAdd=cBuilder.RailStationGrow(road.TargetStation.s_ID, road.Target_RailEntry, false);
return canAdd;
}
function cRoute::DiscoverWorldTiles()
// look at the map and discover what we own, use after loading
{
DInfo("Looking for our properties, game may get frozen for some times on huge maps, be patient",0);
local allmap=AITileList();
local maxTile=AIMap.GetTileIndex(AIMap.GetMapSizeX()-2, AIMap.GetMapSizeY()-2);
AIController.Sleep(1);
allmap.AddRectangle(AIMap.GetTileIndex(1,1), maxTile);
AIController.Sleep(1);
allmap.Valuate(AITile.GetOwner);
AIController.Sleep(1);
local weare=AICompany.ResolveCompanyID(AICompany.COMPANY_SELF);
allmap.KeepValue(weare);
cRoute.RouteDamage.AddList(allmap);
}
DictatorAI/handler/checks.nut 0000644 0001750 0000144 00000044422 12203745354 015521 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
// all operations here are cBuilder even the file itself do handling work
// operations here are time eater
function cBuilder::WeeklyChecks()
{
local week=AIDate.GetCurrentDate();
if (week - INSTANCE.OneWeek < 7) return false;
INSTANCE.OneWeek=AIDate.GetCurrentDate();
DInfo("Weekly checks run...",1);
cCarrier.Process_VehicleWish();
}
function cBuilder::MonthlyChecks()
{
local month=AIDate.GetMonth(AIDate.GetCurrentDate());
if (INSTANCE.OneMonth!=month) { INSTANCE.OneMonth=month; INSTANCE.SixMonth++; }
else return false;
DInfo("Montly checks run...",1);
INSTANCE.main.route.VirtualAirNetworkUpdate();
INSTANCE.main.builder.RouteNeedRepair();
if (INSTANCE.buildDelay > 0) INSTANCE.buildDelay--; // lower delaying timer
INSTANCE.main.carrier.CheckOneVehicleOfGroup(false); // add 1 vehicle of each group
INSTANCE.main.carrier.VehicleMaintenance();
INSTANCE.main.builder.RoadStationsBalancing();
INSTANCE.main.route.DutyOnRoute();
if (INSTANCE.SixMonth == 2) INSTANCE.main.builder.BoostedBuys();
if (INSTANCE.SixMonth == 2) INSTANCE.main.builder.BridgeUpgrader();
if (INSTANCE.SixMonth == 6) INSTANCE.main.builder.HalfYearChecks();
}
function cBuilder::HalfYearChecks()
{
INSTANCE.SixMonth=0;
INSTANCE.TwelveMonth++;
DInfo("Half year checks run...",1);
if (cCarrier.VirtualAirRoute.len() > 1)
{
local maillist=AIVehicleList_Group(cRoute.GetVirtualAirMailGroup());
local passlist=AIVehicleList_Group(cRoute.GetVirtualAirPassengerGroup());
local totair=maillist.Count()+passlist.Count();
DInfo("Aircraft network have "+totair+" aircrafts running on "+cCarrier.VirtualAirRoute.len()+" airports",0);
}
INSTANCE.main.builder.CheckRouteStationStatus();
if (INSTANCE.TwelveMonth == 2) INSTANCE.main.builder.YearlyChecks();
}
function cBuilder::RouteIsDamage(idx)
// Set the route idx as damage
{
local road=cRoute.Load(idx);
if (!road) return;
if (road.VehicleType != AIVehicle.VT_ROAD) return;
if (road.Status != RouteStatus.WORKING) return;
if (!INSTANCE.main.route.RouteDamage.HasItem(idx)) INSTANCE.main.route.RouteDamage.AddItem(idx,0);
}
function cBuilder::RouteNeedRepair()
{
DInfo("Damage routes: "+INSTANCE.main.route.RouteDamage.Count(),1);
if (INSTANCE.main.route.RouteDamage.IsEmpty()) return;
local deletethatone=-1;
local runLimit=2; // number of routes to repair per run
local temp_dmg = AIList();
temp_dmg.AddList(cRoute.RouteDamage);
foreach (routes, state in temp_dmg)
{
if (state == RouteStatus.DEAD) continue; // dead route state
runLimit--;
local trys=state;
trys++;
DInfo("Trying to repair route #"+routes+" for the "+trys+" time",1);
local test=INSTANCE.main.builder.CheckRoadHealth(routes);
if (test) INSTANCE.main.route.RouteDamage.SetValue(routes, -1)
else INSTANCE.main.route.RouteDamage.SetValue(routes, trys);
if (trys >= 3) {
DInfo("Sending all vehicles to depot");
cCarrier.VehicleSendToDepotAndSell(routes);
}
if (trys == 4) {
DInfo("Removing all depots to rebuild them",1);
cTrack.DestroyDepot(cRoute.GetDepot(routes, 1));
cTrack.DestroyDepot(cRoute.GetDepot(routes, 2));
}
if (trys >= 6) { deletethatone=routes }
if (runLimit <= 0) break;
}
INSTANCE.main.route.RouteDamage.RemoveValue(-1);
if (deletethatone != -1)
{
local trys=cRoute.GetRouteObject(deletethatone);
if (trys != null && trys instanceof cRoute)
{
DInfo("RouteNeedRepair mark "+trys.UID+" undoable",1);
trys.RouteIsNotDoable();
}
}
}
function cBuilder::YearlyChecks()
{
INSTANCE.TwelveMonth=0;
DInfo("Yearly checks run...",1);
INSTANCE.main.jobs.CheckTownStatue();
// INSTANCE.main.carrier.do_profit.Clear(); // TODO: Keep or remove that, it's not use yet
INSTANCE.main.carrier.CheckOneVehicleOfGroup(true); // send all vehicles to maintenance check
}
function cBuilder::CheckRouteStationStatus(onlythisone=null)
// This check that our routes are still working, a dead station might prevent us to keep the job done
// pass a stationID to onlythisone to only check that station ID
{
local allstations=AIStationList(AIStation.STATION_ANY);
if (onlythisone != null) allstations.KeepValue(onlythisone);
local healthy = true;
foreach (stationID, dummy in allstations)
{
local pause1 = cLooper();
local stobj = cStation.Load(stationID);
if (!stobj) continue;
if (stobj.s_Owner.IsEmpty()) { DInfo("Trying to remove unused station "+stobj.s_Name,1); cBuilder.DestroyStation(stationID); continue; }
foreach (uid, odummy in stobj.s_Owner)
{
local pause2 = cLooper();
local road=cRoute.Load(uid);
if (!road) continue;
if (road.Status != RouteStatus.WORKING) continue; // avoid non finish routes
local cargoID=road.CargoID;
if (road.VehicleType >= RouteType.AIR) cargoID=cCargo.GetPassengerCargo(); // always check passenger to avoid mail cargo
if (road.SourceStation.s_ID == stobj.s_ID)
{
if (!stobj.IsCargoProduce(cargoID))
{ // we need that cargo produce but it's not
DInfo("Station "+stobj.s_Name+" no longer produce "+cCargo.GetCargoLabel(cargoID),0);
DInfo("CheckRouteStationStatus mark "+road.UID+" undoable",1);
road.RouteIsNotDoable();
healthy = false;
continue;
}
}
if (road.TargetStation.s_ID == stobj.s_ID)
{
if (!stobj.IsCargoAccept(cargoID))
{ // we need that cargo accept but it's not
DInfo("Station "+stobj.s_Name+" no longer accept "+cCargo.GetCargoLabel(cargoID),0);
DInfo("CheckRouteStationStatus mark "+road.UID+" undoable",1);
road.RouteIsNotDoable();
healthy = false;
continue;
}
}
}
}
return healthy;
}
function cBuilder::RoadStationsBalancing()
// Look at road stations for busy loading and balance it by sending vehicle to servicing
// Because vehicle could block the station waiting to load something, while others carrying products can't enter it
{
local allstations = AIStationList(AIStation.STATION_TRUCK_STOP);
if (allstations.IsEmpty()) return;
foreach (stations, _ in allstations)
{
local pause = cLooper();
local s = cStation.Load(stations);
if (!s) continue;
DInfo("Checking station "+s.s_Name,1);
local truck_atstation=cCarrier.VehicleNearStation(stations); // find if anyone is near the station
if (truck_atstation.Count() < 2) continue;
local truck_loading=AIList();
local truck_waiting=AIList();
truck_loading.AddList(truck_atstation);
truck_waiting.AddList(truck_atstation);
truck_loading=cCarrier.VehicleList_KeepLoadingVehicle(truck_loading);
truck_waiting=cCarrier.VehicleList_KeepStuckVehicle(truck_waiting);
local truck_getter_loading=AIList();
local truck_getter_waiting=AIList();
local truck_dropper_loading=AIList();
local truck_dropper_waiting=AIList();
DInfo(" Size: "+s.s_Tiles.Count(),1);
s.UpdateStationInfos();
DInfo(" infos: produce="+s.s_CargoProduce.Count()+" accept="+s.s_CargoAccept.Count(),1);
if (s.s_CargoProduce.Count()==0 && s.s_CargoAccept.Count()==0) { cBuilder.CheckRouteStationStatus(stations); continue; }
// pfff, now we know what cargo that station can use (accept or produce)
// station_produce_cargo.Valuate(AICargo.GetTownEffect);
// station_produce_cargo.RemoveValue(AICargo.TE_PASSENGERS);
// station_accept_cargo.Valuate(AICargo.GetTownEffect);
// station_accept_cargo.RemoveValue(AICargo.TE_PASSENGERS);
// now we can found what vehicles are trying to do
foreach (cargotype, _cdummy in s.s_CargoProduce)
{
local cpause = cLooper();
truck_loading.Valuate(AIVehicle.GetCapacity, cargotype);
foreach (vehicle, capacity in truck_loading)
{
local crg=AIVehicle.GetCargoLoad(vehicle, cargotype);
if (capacity > 0 && !truck_getter_loading.HasItem(vehicle)) truck_getter_loading.AddItem(vehicle, crg);
}
truck_waiting.Valuate(AIVehicle.GetCapacity,cargotype);
foreach (vehicle, capacity in truck_waiting)
{
local crg=AIVehicle.GetCargoLoad(vehicle, cargotype);
if (capacity > 0 && !truck_getter_waiting.HasItem(vehicle)) truck_getter_waiting.AddItem(vehicle, crg);
}
}
// redo with acceptance
foreach (cargotype, dummy in s.s_CargoAccept)
{
local apause = cLooper();
truck_loading.Valuate(AIVehicle.GetCapacity,cargotype);
foreach (vehicle, capacity in truck_loading)
{
local crg=AIVehicle.GetCargoLoad(vehicle, cargotype);
if (capacity > 0 && !truck_dropper_loading.HasItem(vehicle)) truck_dropper_loading.AddItem(vehicle, crg);
// badly name, a dropper loading at station is in fact unloading :p
}
truck_waiting.Valuate(AIVehicle.GetCapacity,cargotype);
foreach (vehicle, capacity in truck_waiting)
{
local crg=AIVehicle.GetCargoLoad(vehicle, cargotype);
if (capacity > 0 && !truck_dropper_waiting.HasItem(vehicle)) truck_dropper_waiting.AddItem(vehicle, crg);
}
}
// we have our 4 lists now, let's play with them
// case 1, station got loader, more loaders are waiting, not harmful -> also vehicle handling will sell them
// case 2, station got loader, and dropper are waiting, bad
// case 3, station got dropper, and loader are waiting, not harmful
// case 4, station got dropper, more dropper are waiting, not harmful
local all_getter=AIList();
all_getter.AddList(truck_getter_loading);
all_getter.AddList(truck_getter_waiting);
local numwait=truck_getter_waiting.Count()+truck_dropper_waiting.Count();
local numload=truck_getter_loading.Count();
local numunload=truck_dropper_loading.Count();
local numdrop=truck_dropper_loading.Count();
DInfo(" Station "+s.s_Name+" have "+numload+" vehicle loading, "+numunload+" vehicle unloading, "+truck_getter_waiting.Count()+" vehicle waiting to load, "+truck_dropper_waiting.Count()+" waiting to unload",1);
if (truck_getter_loading.Count() > 0)
{
if (truck_dropper_waiting.Count() > 0)
{ // send all loader to depot to free space for droppers
foreach (vehicle, load in all_getter)
{
local anotherpause = cLooper();
if (load == 0)
{ // don't push the vehicle that is loading
DInfo("Pushing vehicle "+cCarrier.GetVehicleName(vehicle)+" out of the station to free space for unloaders",1);
AIVehicle.SendVehicleToDepotForServicing(vehicle);
AIVehicle.ReverseVehicle(vehicle);
continue; // stop checks as droppers are waiting because station is busy with getters
}
}
}
}
if (truck_getter_waiting.Count() > 0)
foreach (stacargo, amount_wait in s.s_CargoProduce)
{
local anotherpause = cLooper();
DInfo("Station "+s.s_Name+" produce "+cCargo.GetCargoLabel(stacargo)+" with "+amount_wait+" units waiting",1);
foreach (vehicle, vehcargo in truck_getter_waiting)
{
if (amount_wait > 0) continue; // no action if we have cargo waiting at the station
local aapause = cLooper();
if (AIVehicle.GetCapacity(vehicle, stacargo)==0) continue; // not a vehicle using that cargo
if (AIVehicle.GetAge(vehicle) < 30) continue; // ignore young vehicle
if (DictatorAI.GetSetting("station_balance"))
{
DInfo("Selling vehicle "+cCarrier.GetVehicleName(vehicle)+" to balance station",1);
cCarrier.VehicleSendToDepot(vehicle, DepotAction.SELL);
}
}
}
}
}
function cBuilder::BoostedBuys()
// this function check if we can boost a buy by selling our road vehicles
{
local airportList=AIStationList(AIStation.STATION_AIRPORT);
local waitingtimer=0;
local vehlist = AIVehicleList();
vehlist.RemoveList(cCarrier.ToDepotList);
if (airportList.Count() < 2 && vehlist.Count()>45)
{ // try to boost a first air route creation
local goalairport=cJobs.CostTopJobs[AIVehicle.VT_AIR];
DWarn("Waiting to get an aircraft job. Current ="+INSTANCE.main.carrier.warTreasure+" Goal="+goalairport,1);
if (INSTANCE.main.carrier.warTreasure > goalairport && goalairport > 0)
{
local money=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
local money_goal=money+goalairport
DInfo("Trying to get an aircraft job done",1);
INSTANCE.main.carrier.CrazySolder(goalairport);
do {
AIController.Sleep(74);
INSTANCE.main.carrier.VehicleIsWaitingInDepot(true);
waitingtimer++;
money=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
DInfo("Still "+(money_goal - money)+" to raise",1);
}
while (waitingtimer < 200 && !cBanker.CanBuyThat(goalairport));
if (waitingtimer < 200) DInfo("Operation should success...",1);
INSTANCE.main.carrier.VehicleIsWaitingInDepot(true);
INSTANCE.buildDelay=0; // remove any build blockers
INSTANCE.main.carrier.vehicle_cash = 0;
INSTANCE.main.carrier.vehicle_wishlist.Clear();
}
return;
}
local trainList=AIStationList(AIStation.STATION_TRAIN);
local goaltrain=cJobs.CostTopJobs[AIVehicle.VT_RAIL];
if (vehlist.Count()>45 && goaltrain > 0 && trainList.Count() < 2)
{ // try boost train job buys
DWarn("Waiting to build a train job. Current ="+INSTANCE.main.carrier.warTreasure+" Goal="+goaltrain,1);
local money=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
local money_goal=money+goaltrain;
if (INSTANCE.main.carrier.warTreasure > goaltrain && goaltrain > 0)
{
DInfo("Trying to raise money to buy a new train job",1);
INSTANCE.main.carrier.CrazySolder(goaltrain);
do {
AIController.Sleep(74);
INSTANCE.main.carrier.VehicleIsWaitingInDepot(true);
money=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
DInfo("Still "+(money_goal - money)+" to raise",1);
waitingtimer++;
}
while (waitingtimer < 200 && !cBanker.CanBuyThat(goaltrain));
INSTANCE.main.carrier.VehicleIsWaitingInDepot(true);
INSTANCE.buildDelay=0; // remove any build blockers
INSTANCE.main.carrier.vehicle_cash = 0;
INSTANCE.main.carrier.vehicle_wishlist.Clear();
}
return;
}
local aircraftnumber=AIVehicleList();
aircraftnumber.Valuate(AIVehicle.GetVehicleType);
aircraftnumber.KeepValue(AIVehicle.VT_AIR);
if (aircraftnumber.Count() < 6 && airportList.Count() > 1 && vehlist.Count()>45)
{ // try boost aircrafts buys until we have 6
local goal=INSTANCE.main.carrier.highcostAircraft+(INSTANCE.main.carrier.highcostAircraft * 0.1);
local money=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
local money_goal=money+goal;
DWarn("Waiting to buy of new aircraft. Current ="+INSTANCE.main.carrier.warTreasure+" Goal="+goal,1);
if (INSTANCE.main.carrier.warTreasure > goal && goal > 0)
{
DInfo("Trying to buy a new aircraft",1);
INSTANCE.main.carrier.CrazySolder(goal);
do {
AIController.Sleep(74);
INSTANCE.main.carrier.VehicleIsWaitingInDepot(true);
money=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
DInfo("Still "+(money_goal - money)+" to raise",1);
waitingtimer++;
}
while (waitingtimer < 200 && !cBanker.CanBuyThat(goal));
INSTANCE.main.carrier.VehicleIsWaitingInDepot(true);
INSTANCE.main.carrier.vehicle_cash = INSTANCE.main.carrier.highcostAircraft;
}
return;
}
local goaltrain=INSTANCE.main.carrier.highcostTrain;
if (vehlist.Count()>45 && goaltrain > 0)
{ // try boost train buys
DWarn("Waiting to buy a new train. Current ="+INSTANCE.main.carrier.warTreasure+" Goal="+goaltrain,1);
local money=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
local money_goal=money+goaltrain;
if (INSTANCE.main.carrier.warTreasure > goaltrain)
{
DInfo("Trying to raise money to buy a new train job",1);
INSTANCE.main.carrier.CrazySolder(goaltrain);
do {
AIController.Sleep(74);
INSTANCE.main.carrier.VehicleIsWaitingInDepot(true);
money=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
DInfo("Still "+(money_goal - money)+" to raise",1);
waitingtimer++;
}
while (waitingtimer < 200 && !cBanker.CanBuyThat(goaltrain));
INSTANCE.main.carrier.VehicleIsWaitingInDepot(true);
INSTANCE.buildDelay=0; // remove any build blockers
INSTANCE.main.carrier.vehicle_cash = INSTANCE.main.carrier.highcostTrain;
}
}
}
function cBuilder::BridgeUpgrader()
// Upgrade bridge we own and if it's need
{
local RoadBridgeList=AIList();
RoadBridgeList.AddList(cBridge.BridgeList);
RoadBridgeList.Valuate(cBridge.GetMaxSpeed);
local RailBridgeList=AIList();
RailBridgeList.AddList(RoadBridgeList);
RoadBridgeList.Valuate(cBridge.IsRoadBridge);
RoadBridgeList.KeepValue(1);
RailBridgeList.Valuate(cBridge.IsRailBridge);
RailBridgeList.KeepValue(1);
local numRail=RailBridgeList.Count();
local numRoad=RoadBridgeList.Count();
RoadBridgeList.KeepBelowValue(INSTANCE.main.carrier.speed_MaxRoad); // Keep only too slow bridges
RailBridgeList.KeepBelowValue(INSTANCE.main.carrier.speed_MaxTrain);
local workBridge=AIList();
local twice=false;
local neededSpeed=0;
local btype=0;
local everyone = AIController.GetSetting("upgrade_townbridge");
local weare=AICompany.ResolveCompanyID(AICompany.COMPANY_SELF);
local updateCount = 0;
DInfo("We knows "+numRail+" rail bridges and "+numRoad+" road bridges",0);
do {
workBridge.Clear();
if (!twice) { workBridge.AddList(RoadBridgeList); neededSpeed=INSTANCE.main.carrier.speed_MaxRoad; btype=AIVehicle.VT_ROAD; }
else { workBridge.AddList(RailBridgeList); neededSpeed=INSTANCE.main.carrier.speed_MaxTrain; btype=AIVehicle.VT_RAIL; }
foreach (bridgeUID, speed in workBridge)
{
local thatbridge=cBridge.Load(bridgeUID);
if (thatbridge.owner != -1 && thatbridge.owner != weare) continue;
// only upgrade our or town bridge
if (thatbridge.owner == -1 && !everyone) continue;
// don't upgrade all bridges in one time, we're kind but we're not l'abbé Pierre!
local speederBridge=cBridge.GetCheapBridgeID(btype, thatbridge.length, false);
local oldbridge=AIBridge.GetName(thatbridge.bridgeID);
if (speederBridge != -1)
{
local nbridge=AIBridge.GetName(speederBridge);
local nspeed=AIBridge.GetMaxSpeed(speederBridge);
INSTANCE.main.bank.RaiseFundsBy(AIBridge.GetPrice(speederBridge,thatbridge.length));
if (AIBridge.BuildBridge(btype, speederBridge, thatbridge.firstside, thatbridge.otherside))
{
DInfo("Upgrade "+oldbridge+" to "+nbridge+". We can now handle upto "+nspeed+"km/h",0);
if (thatbridge.owner != weare) everyone = false;
updateCount++;
}
}
if (updateCount == 4) { break; }
}
twice=!twice;
} while (twice);
}
DictatorAI/info.nut 0000755 0001750 0000144 00000011424 12203461031 013561 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
require("class/version.nut");
class DictatorAI extends AIInfo
{
function GetAuthor() { return "Krinn"; }
function GetName() { return "DictatorAI"; }
function GetDescription() { return "a (should be) competitive AI."; }
function GetVersion() { return SELF_VERSION; }
function MinVersionToLoad() { return 1; }
function GetDate() { return "2010-12-26"; }
function CreateInstance() { return "DictatorAI"; }
function GetShortName() { return "DCTR"; }
function GetAPIVersion() { return "1.3"; }
function GetURL() { return "http://www.tt-forums.net/viewtopic.php?f=65&t=52982"; }
function GetSettings() {
AddSetting({name = "PresidentName",
description = "Use real names for the company president?",
easy_value = 0,
medium_value = 0,
hard_value = 0,
custom_value = 0,
flags = CONFIG_BOOLEAN
});
AddSetting({name = "fairlevel",
description = "Alter how the AI act with others",
min_value = 0,
max_value = 2,
easy_value = 0,
medium_value = 1,
hard_value = 2,
custom_value = 2,
flags = CONFIG_NONE
});
AddLabels("fairlevel", {_0 = "Lazy", _1 = "Opportunist", _2 = "Dictator"});
AddSetting({name = "allowedjob",
description = "What tasks the AI can handle",
min_value = 0,
max_value = 2,
easy_value = 1,
medium_value = 0,
hard_value = 0,
custom_value = 0,
flags = CONFIG_INGAME
});
AddLabels("allowedjob", {_0 = "Industry & Town", _1 = "Only Industry", _2 = "Only Town"});
AddSetting({
name = "use_road",
description = "Use buses & trucks",
easy_value = 1,
medium_value = 1,
hard_value = 1,
custom_value = 1,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "station_balance",
description = "Detect traffic jam: on - better fairplay and aesthetic / off - better for economy",
easy_value = 1,
medium_value = 1,
hard_value = 0,
custom_value = 1,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "keep_road",
description = "Remove roads: on - better economy and aesthetic / off - better for opponents (some AIs may bug)",
easy_value = 0,
medium_value = 0,
hard_value = 1,
custom_value = 1,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "use_train",
description = "Use trains",
easy_value = 0,
medium_value = 1,
hard_value = 1,
custom_value = 1,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "use_nicetrain",
description = "Respect newGRF flag: on - better for aesthetic / off - better for economy",
easy_value = 1,
medium_value = 1,
hard_value = 0,
custom_value = 1,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "use_air",
description = "Use aircrafts & choppers",
easy_value = 0,
medium_value = 1,
hard_value = 1,
custom_value = 1,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "use_boat",
description = "Use boats",
easy_value = 1,
medium_value = 1,
hard_value = 1,
custom_value = 1,
flags = AICONFIG_BOOLEAN | AICONFIG_INGAME
});
AddSetting({
name = "use_terraform",
description = "Allow terraforming: always disable in Lazy mode",
easy_value = 0,
medium_value = 1,
hard_value = 1,
custom_value = 1,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "upgrade_townbridge",
description = "Upgrade towns bridges: on - better for aesthetics & opponents / off - better for economy",
easy_value = 1,
medium_value = 1,
hard_value = 0,
custom_value = 0,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "allow_scp",
description = "Allow DictatorAI to play with GoalScript using SCP",
easy_value = 1,
medium_value = 1,
hard_value = 1,
custom_value = 1,
flags = CONFIG_BOOLEAN | CONFIG_INGAME
});
AddSetting({
name = "debug",
description = "Enable debug messages",
min_value = 0,
max_value = 4,
easy_value = 0,
medium_value = 0,
hard_value = 0,
custom_value = 0,
flags = CONFIG_INGAME | CONFIG_DEVELOPER
});
//AddLabels("debug", {_0 = "Disable debug", _1 = "Basic", _2 = "Medium", _3 = "Noisy"});
AddSetting({
name = "debug_sign",
description = "Enable debug signs",
easy_value = 0,
medium_value = 0,
hard_value = 0,
custom_value = 0,
flags = CONFIG_BOOLEAN | CONFIG_INGAME | CONFIG_DEVELOPER
});
}
}
RegisterAI(DictatorAI());
DictatorAI/license.txt 0000644 0001750 0000144 00000043103 11740175277 014300 0 ustar krinn users GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
DictatorAI/main.nut 0000755 0001750 0000144 00000025553 12205357325 013576 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
enum RouteType
{
RAIL, // AIVehicle.VT_RAIL
ROAD, // AIVehicle.VT_ROAD
WATER, // AIVehicle.VT_WATER
AIR, // AIVehicle.VT_AIR
AIRMAIL,
AIRNET,
AIRNETMAIL,
SMALLAIR,
SMALLMAIL,
CHOPPER
}
enum AircraftType
{
EFFICIENT,
BEST,
CHOPPER
}
enum DepotAction
{
SELL=0, // to just sell a vehicle
UPGRADE=1, // to upgrade a vehicle
REPLACE=2, // to replace a vehicle, well this should also upgrade it
CRAZY=3, // to get a big amount of money
REMOVEROUTE=4, // to remove a route
ADDWAGON=500 // to add a train or wagons to a route
LINEUPGRADE=1000, // to upgrade a train route (passing the StationID with it), passing using DepotAction.LINEUPGRADE+StationID
SIGNALUPGRADE=5000, // when a station need build signal on rails (passing the StationID with it)
WAITING=9000, // it's a state send a vehicle to depot and wait x iterations of vehicle in depot check (passing x with it)
}
enum RouteStatus
{
// from 0 to 7 are use for build steps
DAMAGE = 99, // a damage route
WORKING = 100, // all fine
DEAD = -666 // will get removed
}
const DIR_NE = 2;
const DIR_NW = 0;
const DIR_SE = 1;
const DIR_SW = 3;
import("pathfinder.road", "RoadPathFinder", 4);
import("pathfinder.rail", "RailPathFinder", 1);
import("Library.cEngineLib", "cEngineLib", 6);
require("require.nut");
class DictatorAI extends AIController
{
// settings
use_road = null;
use_train = null;
use_boat = null;
use_air = null;
job_train = null;
job_air = null;
job_road = null;
job_boat = null;
terraform = null;
fairlevel = null;
debug = null;
// other
minRank = null;
buildDelay=null;
OneMonth=null;
OneWeek=null;
SixMonth=null;
TwelveMonth=null;
loadedgame = null;
safeStart=null;
main=null;
constructor()
{
::INSTANCE <- this;
minRank = 5000;
fairlevel = 0;
debug = false;
buildDelay = 0;
OneMonth=0;
OneWeek=0;
SixMonth=0;
TwelveMonth=0;
loadedgame = false;
safeStart=0;
job_air = true;
job_train = true;
job_road = true;
job_boat = true;
main = cMain();
}
}
function DictatorAI::Start()
{
cEngineLib.SetAPIErrorHandling(false);
AICompany.SetAutoRenewStatus(false);
this.CheckCurrentSettings();
main.Init();
main.DInfo("DicatorAI started.",0);
if (loadedgame)
{
main.bank.SaveMoney();
cRoute.DiscoverWorldTiles();
cLoader.LoadingGame();
main.jobs.PopulateJobs();
local stationList=AIList(); // check for no more working station if cargo disapears...
stationList.AddList(AIStationList(AIStation.STATION_ANY));
foreach (stationID, dummy in stationList)
{
cStation.CheckCargoHandleByStation(stationID);
}
INSTANCE.main.route.VirtualAirNetworkUpdate();
DInfo("...Loading game end",0);
}
else
{
main.bank.SaveMoney();
cMisc.SetPresident();
main.jobs.PopulateJobs();
main.jobs.RawJobHandling();
safeStart=5;
}
while (true)
{
this.CheckCurrentSettings();
DWarn("Running the AI in debug mode slowdown the AI and can do random issues !!!",1);
main.bank.CashFlow();
main.CheckAccount();
local dmg = AIList();
dmg.AddList(cRoute.RouteDamage);
dmg.KeepValue(RouteStatus.DEAD);
foreach (uid, _ in dmg) cRoute.RouteUndoableFreeOfVehicle(uid);
if (main.SCP.IsAllow()) { main.SCP.Check(); }
if (main.bank.canBuild)
{
if (main.builder.building_route == -1) { main.builder.building_route=main.jobs.GetNextJob(); }
if (main.builder.building_route != -1)
{
main.builder.DumpTopJobs(); // debug
local jobs_obj=cJobs.Load(main.builder.building_route);
main.route=cRoute.GetRouteObject(main.builder.building_route);
if (main.route == null)
{
main.route=cRoute();
if (!jobs_obj) { main.builder.building_route=-1; }
else
{
main.route.CreateNewRoute(main.builder.building_route);
DInfo("Creating a new route : "+cRoute.GetRouteName(main.builder.building_route),0);
}
}
else { DInfo("Construction of route "+cRoute.GetRouteName(main.builder.building_route)+" is at phase "+main.route.Status,1); }
if (main.builder.building_route != -1)
{
main.builder.TryBuildThatRoute();
cMisc.checkHQ();
}
}
}
main.bank.CashFlow();
main.event.HandleEvents();
main.jobs.DeleteIndustry();
AIController.Sleep(1);
cPathfinder.AdvanceAllTasks();
AIController.Sleep(1);
main.builder.WeeklyChecks();
AIController.Sleep(1);
main.builder.MonthlyChecks();
AIController.Sleep(1);
cPathfinder.AdvanceAllTasks();
AIController.Sleep(1);
main.jobs.RawJobHandling();
AIController.Sleep(1);
cPathfinder.AdvanceAllTasks();
AIController.Sleep(1);
cDebug.ClearSigns();
}
}
function DictatorAI::Stop()
{
DInfo("DictatorAI is stopped",0);
ClearSigns();
}
function DictatorAI::NeedDelay(delay=30)
{
if (!debug) { return; }
DInfo("We are waiting: "+delay,2);
::AIController.Sleep(delay);
}
function DictatorAI::Save()
{
// save
local table =
{
stations = null,
virtualpass = null,
virtualmail = null,
}
local all_stations=[];
// stations
foreach(obj in cStation.stationdatabase)
{
if (!AIStation.IsValidStation(obj.s_ID)) { continue; }
all_stations.push(obj.s_ID);
all_stations.push(obj.s_Depot);
if (obj instanceof cStationRail)
{
all_stations.push(obj.s_Train[0]); // STATIONBIT
all_stations.extend(obj.s_EntrySide);
all_stations.extend(obj.s_ExitSide);
}
}
table.stations=all_stations;
local netair=cRoute.VirtualAirGroup[0];
table.virtualpass=netair;
netair=cRoute.VirtualAirGroup[1];
table.virtualmail=netair;
DInfo("Saving game... "+cRoute.database.len()+" routes, "+cStation.stationdatabase.len()+" stations");
return table;
}
function DictatorAI::Load(version, data)
{
DInfo("Loading a saved game with DictatorAI version "+version,0);
if ("stations" in data) { main.bank.unleash_road=data.stations; }
if ("virtualmail" in data) { TwelveMonth=data.virtualmail; }
if ("virtualpass" in data) { main.bank.mincash=data.virtualpass; }
main.carrier.vehicle_cash = version;
loadedgame = true;
}
function DictatorAI::CheckCurrentSettings()
{
// this are settings we should take care of (one day ^^ )
// max_bridge_length = 64
// max_tunnel_length
// join_stations = true
// adjacent_stations = true
debug=true;
use_road=false;
use_train=false;
use_boat=false;
use_air=false;
terraform=false;
if (AIController.GetSetting("debug") == 0) { debug=false; }
fairlevel = DictatorAI.GetSetting("fairlevel");
if (AIController.GetSetting("use_road") && !AIGameSettings.IsDisabledVehicleType(AIVehicle.VT_ROAD)) { use_road = true; }
if (AIController.GetSetting("use_train") && !AIGameSettings.IsDisabledVehicleType(AIVehicle.VT_RAIL)) { use_train = true; }
if (AIController.GetSetting("use_boat") && !AIGameSettings.IsDisabledVehicleType(AIVehicle.VT_WATER)) { use_boat = true; }
if (AIController.GetSetting("use_air") && !AIGameSettings.IsDisabledVehicleType(AIVehicle.VT_AIR)) { use_air = true; }
if (AIController.GetSetting("use_terraform")) { terraform = true; }
main.carrier.VehicleCountUpdate();
if (cCarrier.GetVehicleCount(AIVehicle.VT_ROAD)+1 > AIGameSettings.GetValue("vehicle.max_roadveh")) { use_road = false; job_road = false; }
if (cCarrier.GetVehicleCount(AIVehicle.VT_RAIL)+1 > AIGameSettings.GetValue("vehicle.max_trains")) { use_train = false; job_train = false; }
if (main.carrier.GetVehicleCount(AIVehicle.VT_AIR)+1 > AIGameSettings.GetValue("vehicle.max_aircraft")) { use_air = false; job_air = false; }
if (main.carrier.GetVehicleCount(AIVehicle.VT_WATER) + 1 > AIGameSettings.GetValue("vehicle.max_ships")) { use_boat = false; job_boat = false; }
main.carrier.train_length = AIGameSettings.GetValue("max_train_length");
if (main.carrier.train_length > 5) { main.carrier.train_length = 5; }
if (main.carrier.train_length < 3) { use_train = false; job_train = false; }
switch (fairlevel)
{
case 0: // easiest
main.carrier.road_max_onroute=8;
main.carrier.road_max=2;
main.carrier.road_upgrade=10;
main.carrier.rail_max=1;
main.carrier.water_max=2;
main.carrier.air_max=10;
main.carrier.airnet_max=2;
terraform = false; // no terraforming in easy difficulty
break;
case 1:
main.carrier.road_max_onroute=20;
main.carrier.road_max=3;
main.carrier.road_upgrade=10;
main.carrier.rail_max=4;
main.carrier.water_max=20;
main.carrier.air_max=20;
main.carrier.airnet_max=3;
break;
case 2:
main.carrier.road_max_onroute=40; // upto 40 bus/truck per route
main.carrier.road_max=9; // upto a 9 size road station
main.carrier.road_upgrade=10; // upgrade road station every X vehicles. station can handle so a max that*road_max vehicle
main.carrier.rail_max=12; // it's our highest train limit, can't build more than 12 platforms per station
main.carrier.water_max=100; // there's no real limit for boats
main.carrier.air_max=200; // 8 aircrafts / route
main.carrier.airnet_max=4; // 4 aircrafts / airport in the air network, ie: 10 airports = 40 aircrafts
break;
}
local spdcheck=null;
if (AIGameSettings.IsValid("station_spread"))
{
spdcheck=AIGameSettings.GetValue("station_spread");
if (spdcheck < main.carrier.rail_max) { main.carrier.rail_max=spdcheck; }
}
//use_boat=false; // we will handle boats later
if (INSTANCE.safeStart > 0)
{
// Keep only road
use_boat = false;
use_train = false;
use_air = false;
}
}
function DictatorAI::DInfo(putMsg, debugValue=0, func = "Unknown")
// just output AILog message depending on debug level
{
local debugState = DictatorAI.GetSetting("debug");
if (debugState > 0) { func += "-> "; }
else { func=""; }
if (debugValue <= debugState )
{
AILog.Info(func+putMsg);
}
}
function DictatorAI::DError(putMsg, debugValue=1, func = "Unknown")
// just output AILog message depending on debug level
{
local debugState = DictatorAI.GetSetting("debug");
debugValue = 1; // force error message to always appears when debug is on
func+="-> ";
if (debugValue <= debugState )
{
AILog.Error(func+putMsg+" Error:"+AIError.GetLastErrorString());
}
}
function DictatorAI::DWarn(putMsg, debugValue=1, func = "Unknown")
// just output AILog message depending on debug level
{
local debugState = DictatorAI.GetSetting("debug");
if (debugState > 0) { func += "-> "; }
else { func=""; }
if (debugValue <= debugState )
{
AILog.Warning(func+putMsg);
}
}
DictatorAI/readme 0000644 0001750 0000144 00000014454 12203461640 013267 0 ustar krinn users DictatorAI is release under GPLv2 license.
DictatorAI is originaly a fork from Brumi's SimpleAI (version 4, license GPLv2).
Some code is still coming from the good work Brumi puts on it, you might not see it without knowing it at first as i mostly apply my coding style even to brumi's original code to faster its understanding (for me).
But you can keep see who's who while checking code logic and design or thru Brumi's ai code.
His ai page is there : http://www.tt-forums.net/viewtopic.php?f=65&t=44809&sid=324466cb50b1550e83e43014ee93ee0c
Some code also comes from AdmiralAI by Yexo : see http://www.tt-forums.net/viewtopic.php?f=65&t=38057&sid=91f3acf3ecc6ff5aecaea6ad8b08e81a
You need openttd 1.2+ to run this AI.
About the AI options:
- Respect newGRF flag
There's a flag in newgrf that answer if a train can pull certain type of cargo, ie: you can tell a TGV is not able to carry coal cargo, with that set, you won't see a TGV train pulling coal (and that flag for this example should tell the TGV could only pull passengers cargo to met real life usage).
There's also flags to tell what wagons could be attach to what loco engine : so you can allow wagonX for TGV and not wagonY for it: this way TGV could only be use with wagonX.
Now here's the deal, some newGRF have the "puller flag" (it's ai_special_flag, nfo property 08, get thru CanPullCargo) set, but not the "wagon allowed" flag set <--
Why? Well, it's about newGRF author's choice : if he have set the "puller" flag but not the "wagons allowed" flag, it will tell the game that the TGV can pull any wagons, as long as they are passengers wagon. If it doesn't set the "puller" flag but set the "wagons allowed" flag, it won't tell what cargo could be handle but disallow any wagons except ones he has allow to use, kinda the same, except openttd and AI cannot guess what will be the set of allowed cargo to be handle. A bad implementation in my opinion.
And if he has set the "puller" flag + a set of "wagons allowed", he restrict the game/ai/user to only use those set : so if anyone made a passenger cargo that is better/bigger/faster than the ones he have allow, we couldn't use them as they aren't in the "wagons allowed" set.
So best choice, still in my opinion, for an engine newGRF author would be: set the "puller" flag to tell what the engine should pull, but don't set any wagons flag so we then could use any type of wagon that respect the "puller" flag setting, and so, some newer wagons made by another newGRF author. Nice.
Now the AI with this option ON: the AI will read the "puller" flag and respect the newGRF author choices made: if he doesn't allow coal cargo, then the AI won't use that cargo with that engine: now your TGV won't pull coal, but only pull passengers (assuming the "puller" flag is set to passengers only).
And with that option OFF, the AI will discard the "puller" flag, and try each wagons it can find to see if we can use them with the engine : now you can have a TGV pulling coal, that might not be what player would like for real life or aesthetics playing, but for economy (as set in openttd) the TGV is certainly a faster engine than others engine that have their "puller" flag set to use coal, and you endup with a better train. That's what human player also do in game, using a TGV because it's a faster/better engine to pull a cargo that isn't its natural choice at first.
- Upgrade towns bridges:
If set to OFF it's easy, the AI will only take care of its briges, when set to ON the AI will also take care of town briges the AI (or others players) use, so the AI will upgrade these town bridges for the benefits of everyone. As the AI may not use a bridge but will upgrade it for others, in economical point of view, it's a pure lost of money, but from an aesthetical or real life playing, the AI is kind to help others players or AI (without a bridge upgrade function) and the town by providing better speed on bridges.
- Alter how the AI act with others :
Many internals paramaters depend on those options, but to sum up the Dictator mode allow the AI to do anything to battle against others, while the lazzy mode restrict the AI to be kind (like not building anything if someone is doing the job already) and set some hard settings for the AI (disallowing terraforming)... Well, the AI will be a bit stupid, playing the hard way and fairer with anyone, making it far easier to battle against it.
- What tasks the AI can handle :
* Industry & Town: both allow, see bellow
* Industry Only : the AI will be allow to build task that imply taking cargo from an industry and carry it to another industry or town. Industry->Industry or Industry->Town
* Town Only : the AI will be allow to build tasks that imply taking cargo from a town and carry it to another town. Town->Town
- Remove roads:
When the AI create a task, it try to re-use roads to not build too much roads, but if the AI have no choice (no roads exist), the AI will create roads between industry/town.
That setting is to handle what the AI should do when the AI discard a task : if ON: the AI will remove its roads, a great impact on aesthetic and a great impact on the AI economy (if infrastructure is on), but the problem is for leeching AIs that re-use those roads, they could end-up in a stuck situation as their vehicles couldn't then find their destination. If the leeching AI have an option to handle that (rebuilding missing roads parts or recover its vehicles) fine. But an AI without such options might just get stuck : the vehicles cannot reach their destination and might not even been able to reach their depot.
DictatorAI shouldn't have to care about other AIs, that's a great feature for its economy, and it could even be seen as a bonus if another leecher is stuck and need to face consequences of its leeching. But the human player that wish play with AIs may not enjoy an AI get stucks because of that.
So if you know you are playing with AIs that cannot handle it and still want those AIs to be able to play, you could turn this OFF and DictatorAI will keep the roads, will continue to pay maintenance and upgrading bridges on it.
- Allow DictatorAI to play with GoalScript using SCP:
This setting allow DictatorAI to play with supported SCP commandset.
The AI support only NoCargoGoal SCP commandset.
For now, only NoCarGoal provide it : http://www.tt-forums.net/viewtopic.php?f=65&t=62212
I might add support for other commandset later.
DictatorAI/require.nut 0000755 0001750 0000144 00000003076 12203714115 014312 0 ustar krinn users require("class/cloader.nut");
require("class/cclass.nut");
require("class/cprofiling.nut");
require("class/cerror.nut");
require("road/roadpf.nut");
require("class/cvehicle.nut");
require("class/cbuilder.nut");
require("class/cprocess.nut");
require("class/ccargo.nut");
require("build/track.nut");
require("class/cstation.nut");
require("class/cstationrail.nut");
require("class/cstationroad.nut");
require("class/cstationwater.nut");
require("class/cstationair.nut");
require("class/croute.nut");
require("class/cjobs.nut");
require("class/cmisc.nut");
require("class/cevents.nut");
require("handler/routes.nut");
require("train/railpf.nut")
require("handler/pathfinder.nut");
require("handler/bridgehandler.nut");
require("build/builder.nut");
require("class/cdebug.nut");
require("air/airportbuilder.nut");
require("air/aircraftbuilder.nut");
require("water/waterbuilder.nut");
require("water/boatbuilder.nut");
require("train/railbuilder.nut");
require("train/stationchecker.nut");
require("train/trainbuilder.nut");
require("train/railchemin.nut");
require("train/trainhandler.nut");
require("road/roadbuilder.nut");
require("road/truckbuilder.nut");
require("build/stationremover.nut");
require("build/vehiclebuilder.nut");
require("handler/chemin.nut");
require("handler/checks.nut");
require("handler/vehiclehandler.nut");
require("handler/ordershandler.nut");
//require("handler/stationhandler.nut");
require("handler/enginehandler.nut");
require("class/cbanker.nut");
require("handler/jobs.nut");
require("class/ctile.nut");
require("train/railfollower.nut");
require("class/cscp.nut");
DictatorAI/road/ 0000755 0001750 0000144 00000000000 12204531570 013025 5 ustar krinn users DictatorAI/road/roadpf.nut 0000755 0001750 0000144 00000004240 12203560611 015030 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class MyRoadPF extends RoadPathFinder
{
_cost_level_crossing = null;
}
function MyRoadPF::_Cost(self, path, new_tile, new_direction)
{
local cost = ::RoadPathFinder._Cost(self, path, new_tile, new_direction);
if (AITile.HasTransportType(new_tile, AITile.TRANSPORT_RAIL)) cost += self._cost_level_crossing;
return cost;
}
function MyRoadPF::_Estimate(self, cur_tile, cur_direction, goal_tiles)
{
return 1.4*::RoadPathFinder._Estimate(self, cur_tile, cur_direction, goal_tiles);
}
function MyRoadPF::_GetTunnelsBridges(last_node, cur_node, bridge_dir)
{
local slope = AITile.GetSlope(cur_node);
if (slope == AITile.SLOPE_FLAT && AITile.IsBuildable(cur_node + (cur_node - last_node))) return [];
local tiles = [];
for (local i = 2; i < this._max_bridge_length; i++) {
local bridge_list = AIBridgeList_Length(i + 1);
local target = cur_node + i * (cur_node - last_node);
if (!bridge_list.IsEmpty() && AIBridge.BuildBridge(AIVehicle.VT_ROAD, bridge_list.Begin(), cur_node, target)) {
tiles.push([target, bridge_dir]);
}
}
if (slope != AITile.SLOPE_SW && slope != AITile.SLOPE_NW && slope != AITile.SLOPE_SE && slope != AITile.SLOPE_NE) return tiles;
local other_tunnel_end = AITunnel.GetOtherTunnelEnd(cur_node);
if (!AIMap.IsValidTile(other_tunnel_end)) return tiles;
local tunnel_length = AIMap.DistanceManhattan(cur_node, other_tunnel_end);
local prev_tile = cur_node + (cur_node - other_tunnel_end) / tunnel_length;
if (AITunnel.GetOtherTunnelEnd(other_tunnel_end) == cur_node && tunnel_length >= 2 &&
prev_tile == last_node && tunnel_length < _max_tunnel_length && AITunnel.BuildTunnel(AIVehicle.VT_ROAD, cur_node)) {
tiles.push([other_tunnel_end, bridge_dir]);
}
return tiles;
}
DictatorAI/road/roadbuilder.nut 0000755 0001750 0000144 00000123747 12204531566 016077 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cBuilder::BuildAndStickToRoad(tile, stationtype, stalink = -1)
// Find a road near tile and build a road depot or station connected to that road
//
// @param tile tile where to put the structure
// @param stationtype if AIRoad.ROADVEHTYPE_BUS+100000 build a depot, else build a station of stationtype type
// @param stalink the stationID to link too
// @return -1 on error, tile position on success, CriticalError is set
//
{
local directions=[AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(1, 0), AIMap.GetTileIndex(-1, 0), AIMap.GetTileIndex(0, -1)];
// ok we know we are close to a road, let's find where the road is
local direction=-1;
local tooclose=false;
foreach (voisin in directions)
{
if (AIRoad.IsRoadTile(tile+voisin)) { direction=tile+voisin; break; }
}
if (direction == -1) { DWarn("Can't find a road to stick our structure ???",2); return -1; }
if (stationtype != (AIRoad.ROADVEHTYPE_BUS+100000) && stalink == -1) // not a depot = truck or bus station need
{
foreach (voisin in directions) // find if the place isn't too close from another station
{
tooclose=AITile.IsStationTile(tile+voisin);
if (!tooclose) { tooclose=AITile.IsStationTile(tile+voisin+voisin); }
if (tooclose)
{
DWarn("Road station would be too close from another station",2);
cError.RaiseError(); // force a critical error
return -1;
}
}
}
// now build the structure, function is in stationbuilder.nut
return cBuilder.BuildRoadStationOrDepotAtTile(tile, direction, stationtype, stalink);
}
function cBuilder::BuildRoadDepotAtTile(tile, stationID)
// Try to build a road depot at tile and nearer
{
local reusedepot=cTileTools.GetTilesAroundPlace(tile,24);
reusedepot.Valuate(AITile.GetDistanceManhattanToTile,tile);
reusedepot.Sort(AIList.SORT_BY_VALUE, true);
reusedepot.RemoveAboveValue(8);
reusedepot.Valuate(AITile.IsWaterTile);
reusedepot.KeepValue(0);
reusedepot.Valuate(AITile.IsStationTile);
reusedepot.KeepValue(0);
reusedepot.Valuate(AIRail.IsRailTile);
reusedepot.KeepValue(0);
reusedepot.Valuate(AIRail.IsRailDepotTile);
reusedepot.KeepValue(0);
reusedepot.Valuate(AIRoad.IsRoadDepotTile);
reusedepot.KeepValue(0);
reusedepot.Valuate(AITile.GetSlope);
reusedepot.KeepValue(AITile.SLOPE_FLAT); // only flat tile filtering
reusedepot.Valuate(AIRoad.GetNeighbourRoadCount); // now only keep places stick to a road
reusedepot.KeepAboveValue(0);
reusedepot.Valuate(AIRoad.IsRoadTile);
reusedepot.KeepValue(0);
reusedepot.Valuate(AITile.GetDistanceManhattanToTile,tile);
reusedepot.Sort(AIList.SORT_BY_VALUE, true);
local newpos=-1;
foreach (tile, dummy in reusedepot)
{
newpos=cBuilder.BuildAndStickToRoad(tile, AIRoad.ROADVEHTYPE_BUS+100000, stationID);
if (newpos != -1) { return newpos; }
}
return -1;
}
function cBuilder::BuildRoadStation(start)
// Build a road station for a route
// @param start true to build at source, false at destination
// @return true or false
{
cBanker.RaiseFundsBigTime();
local stationtype = null;
local rad=null;
if (AICargo.GetTownEffect(INSTANCE.main.route.CargoID) == AICargo.TE_PASSENGERS)
{
rad= AIStation.GetCoverageRadius(AIStation.STATION_BUS_STOP);
stationtype = AIRoad.ROADVEHTYPE_BUS;
}
else
{
rad= AIStation.GetCoverageRadius(AIStation.STATION_TRUCK_STOP);
stationtype = AIRoad.ROADVEHTYPE_TRUCK;
}
local dir, tilelist, checklist, otherplace, istown, isneartown=null;
if (start)
{
dir = INSTANCE.main.builder.GetDirection(INSTANCE.main.route.SourceProcess.Location, INSTANCE.main.route.TargetProcess.Location);
if (INSTANCE.main.route.SourceProcess.IsTown)
{
tilelist = cTileTools.GetTilesAroundTown(INSTANCE.main.route.SourceProcess.ID);
checklist= cTileTools.GetTilesAroundTown(INSTANCE.main.route.SourceProcess.ID);
isneartown=true; istown=true;
if (!cTileTools.TownRatingNice(INSTANCE.main.route.SourceProcess.ID))
{
DInfo("Our rating with "+AITown.GetName(INSTANCE.main.route.SourceProcess.ID)+" is too poor",1);
return false;
}
}
else
{
tilelist = AITileList_IndustryProducing(INSTANCE.main.route.SourceProcess.ID, rad);
checklist = AITileList_IndustryProducing(INSTANCE.main.route.SourceProcess.ID, rad);
isneartown=true; // fake it's a town, it produce, it might be within a town (like a bank)
istown=false;
}
otherplace=INSTANCE.main.route.TargetProcess.Location;
}
else
{
dir = INSTANCE.main.builder.GetDirection(INSTANCE.main.route.TargetProcess.Location, INSTANCE.main.route.SourceProcess.Location);
if (INSTANCE.main.route.TargetProcess.IsTown)
{
tilelist = cTileTools.GetTilesAroundTown(INSTANCE.main.route.TargetProcess.ID);
checklist= cTileTools.GetTilesAroundTown(INSTANCE.main.route.TargetProcess.ID);
isneartown=true; istown=true;
if (!cTileTools.TownRatingNice(INSTANCE.main.route.TargetProcess.ID))
{
DInfo("Our rating with "+AITown.GetName(INSTANCE.main.route.TargetProcess.ID)+" is too poor",1);
return false;
}
}
else
{
tilelist = AITileList_IndustryAccepting(INSTANCE.main.route.TargetProcess.ID, rad);
checklist = AITileList_IndustryAccepting(INSTANCE.main.route.TargetProcess.ID, rad);
isneartown=true; istown=false;
}
otherplace=INSTANCE.main.route.SourceProcess.Location;
}
// let's see if we can stick to a road
tilelist.Sort(AIList.SORT_BY_VALUE, false); // highest values first
checklist.Valuate(AIRoad.IsRoadTile);
checklist.KeepValue(1);
if (checklist.IsEmpty())
{
DInfo("Cannot stick our station to a road, building classic",2);
isneartown=false;
}
else
{
DInfo("Sticking station & depot to the road",2);
}
checklist.AddList(tilelist); // re-put tiles in it in case we fail building later
if (isneartown)
{
// first, removing most of the unbuildable cases
tilelist.Valuate(AITile.IsWaterTile);
tilelist.KeepValue(0);
tilelist.Valuate(AITile.IsStationTile);
tilelist.KeepValue(0);
tilelist.Valuate(AIRail.IsRailTile);
tilelist.KeepValue(0);
tilelist.Valuate(AIRail.IsRailDepotTile);
tilelist.KeepValue(0);
tilelist.Valuate(AIRoad.IsRoadDepotTile);
tilelist.KeepValue(0);
tilelist.Valuate(AITile.GetSlope);
tilelist.KeepValue(AITile.SLOPE_FLAT); // only flat tile filtering
tilelist.Valuate(AIRoad.GetNeighbourRoadCount); // now only keep places stick to a road
tilelist.KeepAboveValue(0);
tilelist.Valuate(AIRoad.IsRoadTile);
tilelist.KeepValue(0);
if (!istown && !start) // not a town, and not start = only industry as destination
{
tilelist.Valuate(AIMap.DistanceManhattan, otherplace);
tilelist.Sort(AIList.SORT_BY_VALUE,true); // little distance first
}
else // town or (industry at start)
{
if (!istown)
{
tilelist.Valuate(AITile.GetCargoProduction, INSTANCE.main.route.CargoID, 1, 1, rad);
}
else
{
tilelist.Valuate(AITile.GetCargoAcceptance, INSTANCE.main.route.CargoID, 1, 1, rad);
tilelist.KeepAboveValue(7);
}
tilelist.Sort(AIList.SORT_BY_VALUE, false);
}
}
else
{
if (!istown)
{
tilelist.Valuate(AIMap.DistanceManhattan, otherplace);
}
tilelist.Sort(AIList.SORT_BY_VALUE,true);
}
DInfo("Tilelist set to "+tilelist.Count(),2);
local success = false;
local depotbuild=false;
local stationbuild=false;
local deptile = -1;
local statile = -1;
local stafront = -1;
local staid = -1;
if (isneartown)
{
foreach (tile, dummy in tilelist)
{
statile=cBuilder.BuildAndStickToRoad(tile, stationtype);
if (statile >= 0) { stationbuild = true; break; }
}
if (stationbuild)
{
staid = AIStation.GetStationID(statile);
// try build depot closer to our station
tilelist.Valuate(AITile.GetDistanceManhattanToTile,statile);
tilelist.Sort(AIList.SORT_BY_VALUE, true);
foreach (tile, dummy in tilelist)
{
if (tile == statile) { continue; } // don't build on the same place as our new station
deptile=cBuilder.BuildAndStickToRoad(tile, AIRoad.ROADVEHTYPE_BUS+100000, -1); // depot
if (deptile >= 0) { depotbuild = true; break; }
}
}
}
success=(depotbuild && stationbuild);
if ((statile==-1 || deptile==-1) && !istown && isneartown)
{
// We fail to build the station, but it's because we force build station close to roads and there is no roads
if (statile > 0) { cTileTools.DemolishTile(statile); }
if (deptile > 0) { cTileTools.DemolishTile(deptile); }
isneartown=false;
tilelist.AddList(checklist); // restore the list of original tiles
tilelist.Valuate(AITile.IsBuildable);
tilelist.KeepValue(1);
tilelist.Valuate(AIMap.DistanceManhattan, otherplace);
tilelist.Sort(AIList.SORT_BY_VALUE, true);
}
local s_front = cTileTools.GetForwardRelativeFromDirection(dir);
if (!isneartown)
{
foreach (tile, dummy in tilelist)
{
if (cTileTools.CheckLandForConstruction(tile, 1, 3))
{
if (!stationbuild)
{
statile = cBuilder.BuildRoadStationOrDepotAtTile(tile, tile+s_front, stationtype, AIStation.STATION_NEW);
stationbuild = (statile > 0);
if (stationbuild) { staid = AIStation.GetStationID(statile); }
}
if (!depotbuild && stationbuild)
{
deptile = cBuilder.BuildRoadDepotAtTile(tile, -1);
depotbuild = (deptile > 0);
}
success = (depotbuild && stationbuild);
if (success) { break; }
}
}
}
if (start) { INSTANCE.main.route.SourceStation = staid; }
else { INSTANCE.main.route.TargetStation = staid; }
print("station at "+statile+" ID="+AIStation.GetStationID(statile));
local newStation = INSTANCE.main.route.CreateNewStation(start);
if (newStation == null) { return false; }
newStation.s_Depot = deptile; // attach a depot to that station
cStation.StationClaimTile(deptile, staid);
cBuilder.BuildRoadROAD(AIRoad.GetRoadDepotFrontTile(deptile), AIRoad.GetRoadStationFrontTile(statile), staid);
local stadir = cBuilder.GetDirection(statile, AIRoad.GetRoadStationFrontTile(statile));
local tileFrom= statile + cTileTools.GetRightRelativeFromDirection(stadir);
local tileTo= statile + cTileTools.GetLeftRelativeFromDirection(stadir);
cTileTools.TerraformLevelTiles(tileFrom, tileTo); // try levels mainstation and its neighbourg
tileTo=statile+cTileTools.GetLeftRelativeFromDirection(stadir)+cTileTools.GetForwardRelativeFromDirection(stadir)+cTileTools.GetForwardRelativeFromDirection(stadir);
cTileTools.TerraformLevelTiles(tileFrom, tileTo); // try levels all tiles a station could use to grow
cTileTools.TerraformLevelTiles(tileFrom+cTileTools.GetLeftRelativeFromDirection(stadir), tileTo+cTileTools.GetRightRelativeFromDirection(stadir));
cTileTools.TerraformLevelTiles(tileFrom+cTileTools.GetLeftRelativeFromDirection(stadir), tileTo+cTileTools.GetRightRelativeFromDirection(stadir)+cTileTools.GetForwardRelativeFromDirection(stadir));
return true;
}
function cBuilder::PathfindRoadROAD(head1, head2)
// just pathfind the road, but still don't build it
{
local pathfinder = MyRoadPF();
pathfinder._cost_level_crossing = 1000;
pathfinder._cost_coast = 100;
pathfinder._cost_slope = 100;
pathfinder._cost_bridge_per_tile = 100;
pathfinder._cost_tunnel_per_tile = 80;
pathfinder._max_bridge_length = 20;
pathfinder.InitializePath([head1], [head2]);
local savemoney=AICompany.GetBankBalance(AICompany.COMPANY_SELF);
local pfInfo=null;
INSTANCE.main.bank.SaveMoney(); // thinking long time, don't waste money
pfInfo=AISign.BuildSign(head1,"Pathfinding...");
DInfo("Road Pathfinding...",1);
local path = false;
local counter=0;
while (path == false && counter < 250)
{
path = pathfinder.FindPath(250);
counter++;
AISign.SetName(pfInfo,"Pathfinding... "+counter);
AIController.Sleep(1);
}
// restore our money
INSTANCE.main.bank.RaiseFundsBy(savemoney);
AISign.RemoveSign(pfInfo);
if (path != null && path != false)
{
DInfo("Path found. (" + counter + ")",0);
return path;
}
else
{
cDebug.ClearSigns();
DInfo("Pathfinding failed.",1);
cError.RaiseError();
return false;
}
}
function cBuilder::BuildRoadFrontTile(tile, targettile, stationID)
{
if (!AIRoad.IsRoadTile(targettile))
{
cTileTools.DemolishTile(targettile);
cTrack.BuildTrack_Road(tile, targettile, stationID, false);
}
return AIRoad.AreRoadTilesConnected(tile, targettile);
}
function cBuilder::CheckRoadHealth(routeUID)
// we check a route for trouble & try to solve them
// return true if no problems were found
{
local repair=cRoute.Load(routeUID);
if (!repair) { DInfo("Cannot load that route for a repair, switching to current route.",1); repair=INSTANCE.main.route; }
if (typeof(repair.SourceStation) != "instance" || typeof(repair.TargetStation) != "instance")
{ DInfo("Cannot repair that route as stations aren't setup.",1); return false; }
if (repair.VehicleType != AIVehicle.VT_ROAD) { return false; } // only check road type
if (!cBuilder.CheckRouteStationStatus(repair.SourceStation.s_ID) || !cBuilder.CheckRouteStationStatus(repair.TargetStation.s_ID)) { return true; }
// the route itself will get destroy by CheckRouteStationStatus returning false, so we return the route is ok.
local good=true;
repair.Status=RouteStatus.DAMAGE; // on hold
local space=" ";
local correction=false;
local temp=null;
local minGood=false;
local srcEntries=AIList();
local dstEntries=AIList();
DInfo("Checking route health of "+repair.Name,1);
// check stations for trouble
// source station
correction=false;
local msg="";
local error_repair="Fixed !";
local error_error="Fail to fix it";
temp=repair.SourceStation;
if (!AIStation.IsValidStation(temp.s_ID)) { DInfo(space+" Source Station is invalid !",1); return false; } // critical issue
else { DInfo(space+"Source station "+temp.s_Name+" is valid",1); }
if (good)
{
DInfo(space+space+"Station size : "+temp.s_Tiles.Count(),1);
foreach (tile, front in temp.s_Tiles)
{
cDebug.PutSign(tile, "S");
srcEntries.AddItem(tile, 0);
msg=space+space+"Entry "+tile+" is ";
if (!AIRoad.AreRoadTilesConnected(tile, front))
{
msg+="NOT usable. ";
correction=cBuilder.BuildRoadFrontTile(tile, front, temp.s_ID);
if (correction) { msg+=error_repair; srcEntries.SetValue(tile, 1); }
else { msg+=error_error; good=false; srcEntries.SetValue(tile, -1); }
}
else { msg+="usable"; srcEntries.SetValue(tile, -1); }
DInfo(msg,1);
}
}
cDebug.ClearSigns();
// target station
correction=false;
temp=repair.TargetStation;
if (!AIStation.IsValidStation(temp.s_ID)) { DInfo(space+" Destination Station is invalid !",1); return false; } // critical issue
else { DInfo(space+"Destination station "+temp.s_Name+" is valid",1); }
if (good)
{
DInfo(space+space+"Station size : "+temp.s_Tiles.Count(),1);
foreach (tile, front in temp.s_Tiles)
{
cDebug.PutSign(tile, "S");
dstEntries.AddItem(tile, 0);
msg=space+space+"Entry "+tile+" is ";
if (!AIRoad.AreRoadTilesConnected(tile, front))
{
msg+="NOT usable. ";
correction=cBuilder.BuildRoadFrontTile(tile, front, temp.s_ID);
if (correction) { msg+=error_repair; dstEntries.SetValue(tile, 1); }
else { msg+=error_error; good=false; dstEntries.SetValue(tile, -1); }
}
else { msg+="usable"; dstEntries.SetValue(tile, -1); }
DInfo(msg,1);
}
}
cDebug.ClearSigns();
// check the road itself from source to destination
msg=space+"Connection from source station to target station : "
foreach (stile, sfront in repair.SourceStation.s_Tiles)
{
foreach (dtile, dfront in repair.TargetStation.s_Tiles)
{
msg=space+"Connnection from "+repair.SourceStation.s_Name+" Entry #"+stile+" to "+repair.TargetStation.s_Name+" Entry #"+dtile+" : ";
if (!INSTANCE.main.builder.RoadRunner(sfront, dfront, AIVehicle.VT_ROAD))
{
// Removing depots that might prevents us from reaching our target
cTrack.DestroyDepot(repair.TargetStation.s_Depot);
cTrack.DestroyDepot(repair.SourceStation.s_Depot);
msg+="Damage & ";
INSTANCE.main.builder.BuildRoadROAD(sfront, dfront, repair.SourceStation.s_ID);
if (!INSTANCE.main.builder.RoadRunner(sfront, dfront, AIVehicle.VT_ROAD))
{ msg+=error_error; good=false; }
else { msg+=error_repair; minGood=true; break; }
DInfo(msg,1);
}
else { DInfo(msg+"Working",1); minGood=true; break; }
}
if (minGood) { break; }
cDebug.ClearSigns();
}
// the source depot
msg=space+"Source Depot "+repair.SourceStation.s_Depot+" is ";
if (!AIRoad.IsRoadDepotTile(repair.SourceStation.s_Depot))
{
msg+="invalid. ";
repair.SourceStation.s_Depot = INSTANCE.main.builder.BuildRoadDepotAtTile(repair.SourceStation.GetRoadStationEntry(), repair.SourceStation.s_ID);
if (AIRoad.IsRoadDepotTile(repair.SourceStation.s_Depot)) { msg+=error_repair; }
else { msg+=error_error; good=false; }
}
else { msg+="valid"; }
DInfo(msg,1);
local depotfront=AIRoad.GetRoadDepotFrontTile(repair.SourceStation.s_Depot);
if (good)
{
msg=space+space+"Depot entry is ";
if (!AIRoad.AreRoadTilesConnected(repair.SourceStation.s_Depot, depotfront))
{
msg+="not usable. ";
correction=cBuilder.BuildRoadFrontTile(repair.SourceStation.s_Depot, depotfront, repair.SourceStation.s_ID);
if (correction) { msg+=error_repair; }
else { msg+=error_error; good=false; }
}
else { msg+="usable"; }
DInfo(msg,1);
}
// the destination depot
msg=space+"Destination Depot "+repair.TargetStation.s_Depot+" is ";
if (!AIRoad.IsRoadDepotTile(repair.TargetStation.s_Depot))
{
msg+="invalid. ";
repair.TargetStation.s_Depot = INSTANCE.main.builder.BuildRoadDepotAtTile(repair.TargetStation.GetRoadStationEntry(), repair.TargetStation.s_ID);
if (AIRoad.IsRoadDepotTile(repair.TargetStation.s_Depot)) { msg+=error_repair; }
else { msg+=error_error; good=false; }
}
else { msg+="valid"; }
DInfo(msg,1);
local depotfront=AIRoad.GetRoadDepotFrontTile(repair.TargetStation.s_Depot);
if (good)
{
msg=space+space+"Depot entry is ";
if (!AIRoad.AreRoadTilesConnected(repair.TargetStation.s_Depot, depotfront))
{
msg+="not usable. ";
correction=INSTANCE.main.builder.BuildRoadFrontTile(repair.TargetStation.s_Depot, depotfront, repair.TargetStation.s_ID);
if (correction) { msg+=error_repair; }
else { msg+=error_error; good=false; }
}
else { msg+="usable"; }
DInfo(msg,1);
}
if (good)
{
// if we are still here, both depots are working as they should
local src_depot_front=AIRoad.GetRoadDepotFrontTile(repair.SourceStation.s_Depot);
local tgt_depot_front=AIRoad.GetRoadDepotFrontTile(repair.TargetStation.s_Depot);
// source station validity with its own depot
foreach (tile, front in repair.SourceStation.s_Tiles)
{
msg=space+"Connnection from source station -> Entry "+tile+" to its depot : ";
if (!INSTANCE.main.builder.RoadRunner(front, src_depot_front, AIVehicle.VT_ROAD))
{
msg+="Damage & ";
INSTANCE.main.builder.BuildRoadROAD(front, src_depot_front, repair.SourceStation.s_ID);
if (!INSTANCE.main.builder.RoadRunner(front, src_depot_front, AIVehicle.VT_ROAD))
{ msg+=error_error; good=false; srcEntries.SetValue(tile, -1); }
else { msg+=error_repair; srcEntries.SetValue(tile, 1); }
DInfo(msg,1);
}
else { DInfo(msg+"Working",1); srcEntries.SetValue(tile, 1); }
cDebug.ClearSigns();
}
// target station validity with its own depot
foreach (tile, front in repair.TargetStation.s_Tiles)
{
msg=space+"Connnection from destination station -> Entry "+tile+" to its depot : ";
if (!INSTANCE.main.builder.RoadRunner(front, tgt_depot_front, AIVehicle.VT_ROAD))
{
msg+="Damage & ";
INSTANCE.main.builder.BuildRoadROAD(front, tgt_depot_front, repair.TargetStation.s_ID);
if (!INSTANCE.main.builder.RoadRunner(front, tgt_depot_front, AIVehicle.VT_ROAD))
{ msg+=error_error; good=false; dstEntries.SetValue(tile, -1); }
else { msg+=error_repair; dstEntries.SetValue(tile, 1); }
DInfo(msg,1);
}
else { DInfo(msg+"Working",1); dstEntries.SetValue(tile, 1); }
cDebug.ClearSigns();
}
}
// Now clear out dead station entries
srcEntries.KeepValue(-1);
DInfo("Source station entries : "+repair.SourceStation.s_Tiles.Count()+" working "+srcEntries.Count()+" dead",1);
foreach (tile, _ in srcEntries)
{
DInfo(space+"Removing dead source station -> Entry "+tile,1);
if (cTileTools.DemolishTile(tile)) { repair.SourceStation.s_Tiles.RemoveItem(tile); }
}
dstEntries.KeepValue(-1);
DInfo("Destination station entries : "+repair.TargetStation.s_Tiles.Count()+" working "+dstEntries.Count()+" dead",1);
foreach (tile, _ in dstEntries)
{
DInfo(space+"Removing dead destination station -> Entry "+tile,1);
if (cTileTools.DemolishTile(tile)) { repair.TargetStation.s_Tiles.RemoveItem(tile); }
}
repair.SourceStation.s_Location = AIStation.GetLocation(repair.SourceStation.s_ID);
repair.TargetStation.s_Location = AIStation.GetLocation(repair.TargetStation.s_ID);
cDebug.ClearSigns();
if (minGood || good) { repair.Status=RouteStatus.WORKING; }
return minGood;
}
function cBuilder::RoadFindCompatibleDepot(tile)
// Try to find an existing road depot near tile and reuse it
//
// @param tile the tile to search the depot
// @return -1 on failure, depot location on success
{
local reusedepot=cTileTools.GetTilesAroundPlace(tile,24);
reusedepot.Valuate(AIRoad.IsRoadDepotTile);
reusedepot.KeepValue(1);
reusedepot.Valuate(AITile.GetOwner);
local weare=AICompany.ResolveCompanyID(AICompany.COMPANY_SELF);
reusedepot.KeepValue(weare);
reusedepot.Valuate(AITile.GetDistanceManhattanToTile,tile);
reusedepot.Sort(AIList.SORT_BY_VALUE, true);
reusedepot.RemoveAboveValue(10);
local newdeploc=-1;
if (!reusedepot.IsEmpty())
{
newdeploc=reusedepot.Begin();
}
return newdeploc;
}
function cBuilder::RoadStationNeedUpgrade(roadidx,start)
// Upgrade a road station.
// @param roadidx index of the route to upgrade
// @param start true to upgrade source station, false for destination station
// @return true or false
{
local road=cRoute.Load(roadidx);
if (!road) { return false; }
if (road.Status != RouteStatus.WORKING) { return false; }
cBanker.RaiseFundsBigTime();
local work=null;
if (start) { work=road.SourceStation; }
else { work=road.TargetStation; }
local in_town = false;
if (start) { in_town = road.SourceProcess.IsTown; }
else { in_town = road.TargetProcess.IsTown; }
DInfo("Upgrading road station "+work.s_Name,0);
work.s_DateLastUpgrade = AIDate.GetCurrentDate(); // setup the date in case of failure
local depot_id=work.s_Depot;
DInfo("Road depot is at "+depot_id,2);
// first lookout where is the station, where is its entry, where is the depot, where is the depot entry
local sta_pos=work.s_Location;
local sta_front=AIRoad.GetRoadStationFrontTile(sta_pos);
local dep_pos=depot_id;
local dep_front=AIRoad.GetRoadDepotFrontTile(depot_id);
local depotdead=false;
local statype= AIRoad.ROADVEHTYPE_BUS;
if (work.s_Type == AIStation.STATION_TRUCK_STOP) { statype=AIRoad.ROADVEHTYPE_TRUCK; }
local deptype=AIRoad.ROADVEHTYPE_BUS+100000; // we add 100000
local new_sta_pos=-1;
local new_dep_pos=-1;
local success=false;
local upgradepos=[];
local facing=INSTANCE.main.builder.GetDirection(sta_pos, sta_front);
local p_left = cTileTools.GetPosRelativeFromDirection(0, facing);
local p_right = cTileTools.GetPosRelativeFromDirection(1, facing);
local p_forward = cTileTools.GetPosRelativeFromDirection(2, facing);
local p_backward = cTileTools.GetPosRelativeFromDirection(3, facing);
cDebug.PutSign(sta_pos+p_left,"L");
cDebug.PutSign(sta_pos+p_right,"R");
cDebug.PutSign(sta_pos+p_backward,"B");
cDebug.PutSign(sta_pos+p_forward+p_forward,"F");
DInfo("Size :"+work.s_Size,2);
INSTANCE.NeedDelay(20);
// 1st tile of station, 2nd tile to face
upgradepos.push(sta_pos+p_left); // left of station, same facing
upgradepos.push(sta_pos+p_left+p_forward);
upgradepos.push(sta_pos+p_right); // right of station, same facing
upgradepos.push(sta_pos+p_right+p_forward);
upgradepos.push(sta_pos+p_left); // left of station, facing left
upgradepos.push(sta_pos+p_left+p_left);
upgradepos.push(sta_pos+p_right); // right of station, facing right
upgradepos.push(sta_pos+p_right+p_right);
upgradepos.push(sta_pos+p_forward+p_forward); // front station, facing opposite
upgradepos.push(sta_pos+p_forward);
upgradepos.push(sta_pos+p_forward+p_forward+p_left); // front station, left, facing opposite
upgradepos.push(sta_pos+p_forward+p_left);
upgradepos.push(sta_pos+p_forward+p_forward+p_right); // front station, right, facing opposite
upgradepos.push(sta_pos+p_forward+p_right);
upgradepos.push(sta_pos+p_forward+p_forward+p_right); // front right of station, facing right
upgradepos.push(sta_pos+p_forward+p_forward+p_right+p_right);
upgradepos.push(sta_pos+p_forward+p_forward+p_left); // front left of station, facing left
upgradepos.push(sta_pos+p_forward+p_forward+p_left+p_left);
upgradepos.push(sta_pos+p_backward); // behind station, facing opposite
upgradepos.push(sta_pos+p_backward+p_backward);
upgradepos.push(sta_pos+p_backward+p_left); // behind station, left, facing opposite
upgradepos.push(sta_pos+p_backward+p_left+p_backward);
upgradepos.push(sta_pos+p_backward+p_right); // behind station, right, facing opposite
upgradepos.push(sta_pos+p_backward+p_right+p_backward);
upgradepos.push(sta_pos+p_backward+p_left); // behind station, left, facing left
upgradepos.push(sta_pos+p_backward+p_left+p_left);
upgradepos.push(sta_pos+p_backward+p_right); // behind station, right, facing right
upgradepos.push(sta_pos+p_backward+p_right+p_right);
local allfail=true;
for (local i=0; i < upgradepos.len()-1; i++)
{
local tile=upgradepos[i];
local direction=upgradepos[i+1];
if (AIRoad.IsRoadStationTile(tile) || AIRoad.IsRoadStationTile(direction)) { i++; continue; } // don't build on a station
if (tile == dep_pos || direction == dep_pos) { depotdead = cTrack.DestroyDepot(dep_pos); }
// kill our depot if it is about to bug us building
if (AIRoad.IsRoadTile(tile)) { i++; continue; } // don't build on a road if we could avoid it
cDebug.PutSign(tile, "S");
cDebug.PutSign(direction, "o");
new_sta_pos=cBuilder.BuildRoadStationOrDepotAtTile(tile, direction, statype, work.s_ID);
if (!cError.IsError()) { allfail=false; } // if we have only critical errors we're doom
cError.ClearError(); // discard it
if (new_sta_pos != -1) { break; }
local pause = cLooper();
INSTANCE.NeedDelay(10);
cDebug.ClearSigns();
i++;
}
DInfo("2nd try don't care roads",2);
// same as upper but destructive
if (new_sta_pos == -1 && !in_town)
{
for (local i=0; i < upgradepos.len()-1; i++)
{
local tile=upgradepos[i];
local direction=upgradepos[i+1];
if (AIRoad.IsRoadStationTile(tile) || AIRoad.IsRoadStationTile(direction)) { i++; continue; } // don't build on a station
cDebug.PutSign(tile, "S");
cDebug.PutSign(direction, "o");
if (tile == dep_pos || direction == dep_pos) { depotdead = cTrack.DestroyDepot(dep_pos); }
// kill our depot if it is about to bug us building
if (!cTileTools.DemolishTile(tile)) { DInfo("Cannot clean the place for the new station at "+tile,1); }
new_sta_pos=cBuilder.BuildRoadStationOrDepotAtTile(tile, direction, statype, work.s_ID);
if (!cError.IsError()) { allfail=false; } // if we have only critical errors we're doom
cError.ClearError(); // discard it
if (new_sta_pos != -1) { break; }
AIController.Sleep(1);
INSTANCE.NeedDelay(50);
cDebug.ClearSigns();
i++;
}
}
if (new_sta_pos == dep_front && !depotdead)
{
depotdead=true; // the depot entry is now block by the station
cTrack.DestroyDepot(dep_pos);
}
if (depotdead)
{
DWarn("Road depot was destroy while upgrading",1);
new_dep_pos=INSTANCE.main.builder.BuildRoadDepotAtTile(new_sta_pos, work.s_ID);
work.s_Depot=new_dep_pos;
cError.ClearError();
// Should be more than enough
}
if (new_sta_pos > -1)
{
DInfo("Station "+work.s_Name+" has been upgrade",0);
work.s_Tiles = cTileTools.FindStationTiles(work.s_Location);
work.s_Tiles.Valuate(AIRoad.GetRoadStationFrontTile);
work.s_Size=work.s_Tiles.Count();
DInfo("New station size: "+work.s_Size+"/"+work.s_MaxSize,2);
}
else // fail to upgrade station
{
DInfo("Failure to upgrade "+work.s_Name,1);
if (allfail)
{
work.s_MaxSize = work.s_Size;
DInfo("Cannot upgrade "+work.s_Name+" anymore !",1);
}
success=false;
}
if (!work.s_Owner.IsEmpty()) { INSTANCE.main.builder.RouteIsDamage(work.s_Owner.Begin()); }
// ask ourselves a check for one route that own that station
if (success) { work.s_MoneyUpgrade = 0; work.s_DateLastUpgrade = null; }
return success;
}
function cBuilder::BuildRoadStationOrDepotAtTile(tile, direction, stationtype, stationnew)
// Build a road depot or station, add tile to blacklist on critical failure
// Also build the entry tile with road if need. Try also to find a compatible depot near the wanted position and re-use it
//
// @param tile the tile where to put the structure
// @param direction the tile where the structure will be connected
// @param stationtype if AIRoad.ROADVEHTYPE_BUS+100000 build a depot, else build a station of stationtype type
// @param stationnew invalid station id to build a new station, else joint the station with stationid
// @return tile position on success. -1 on error, set CriticalError
{
// before spending money on a "will fail" structure, check the structure could be connected to a road
if (AITile.IsStationTile(tile)) { return -1; } // don't destroy a station, might even not be our
local claimTile = AIList();
cBanker.RaiseFundsBigTime();
if (!AIRoad.IsRoadTile(direction))
{
if (!cTileTools.DemolishTile(direction))
{
DWarn("Can't remove the tile front structure to build a road at "+direction,2); cDebug.PutSign(direction,"X");
cError.IsCriticalError();
return -1;
}
}
cError.ClearError();
if (!cTileTools.DemolishTile(tile))
{
DWarn("Can't remove the structure tile position at "+tile,2); cDebug.PutSign(tile,"X");
cError.IsCriticalError();
return -1;
}
local success=false;
local newstation=0;
if (AIStation.IsValidStation(stationnew)) { newstation=stationnew; }
else { newstation=AIStation.STATION_NEW; }
if (stationtype == (AIRoad.ROADVEHTYPE_BUS+100000))
{
cBanker.RaiseFundsBigTime();
// first let's hack another depot if we can
local hackdepot = cBuilder.RoadFindCompatibleDepot(tile);
if (hackdepot == -1) { success = AIRoad.BuildRoadDepot(tile, direction); }
else
{
tile=hackdepot;
direction=AIRoad.GetRoadDepotFrontTile(tile);
success=true;
}
if (!success)
{
DWarn("Can't built a road depot at "+tile,2);
cError.IsCriticalError();
}
else
{
if (hackdepot == -1) { DInfo("Built a road depot at "+tile,0); }
else { DInfo("Found a road depot near "+tile+", reusing that one",0); }
}
}
else
{
cBanker.RaiseFundsBigTime(); cDebug.ClearSigns();
DInfo("Road info: "+tile+" direction"+direction+" type="+stationtype+" mod="+newstation,2);
//cDebug.PutSign(tile,"s"); cDebug.PutSign(direction,"c");
success=AIRoad.BuildRoadStation(tile, direction, stationtype, newstation);
if (!success)
{
DWarn("Can't built the road station at "+tile,2);
cError.IsCriticalError();
}
else {
DInfo("Built a road station at "+tile,0);
}
}
if (!success)
{
return -1;
}
else
{
if (!AIRoad.AreRoadTilesConnected(tile, direction))
if (!cTrack.BuildTrack_Road(tile, direction, -1, false))
{
DWarn("Fail to connect the road structure with the road in front of it",2);
cError.IsCriticalError();
if (!cTileTools.DemolishTile(tile))
{
DWarn("Can't remove bad road structure !",2);
}
return -1;
}
local tlist = AIList();
tlist.AddItem(tile, 0);
tlist.AddItem(direction, 0);
return tile;
}
}
function cBuilder::RoadRunnerHelper(source, target, road_type, walkedtiles=null, origin=null)
// Follow all directions to walk through the path starting at source, ending at target
// check if the path is valid by using road_type (railtype, road)
// return solve in an AIList() if we reach target by running the path
{
local Distance = AITile.GetDistanceManhattanToTile;
if (road_type == AIVehicle.VT_WATER) { Distance = cTileTools.GetDistanceChebyshevToTile; }
local max_wrong_direction=15;
if (origin == null) { origin = Distance(source, target); }
if (walkedtiles == null) { walkedtiles=AIList(); }
local valid=false;
local direction=null;
local found=false;
local solve= AIList();
if (source == target) { found = true; }
local directions=[AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(1, 0), AIMap.GetTileIndex(-1, 0), AIMap.GetTileIndex(0, -1)];
local dirswap = AIList();
if (cBridge.IsBridgeTile(source) || AITunnel.IsTunnelTile(source))
{
local endat = cBridge.IsBridgeTile(source) ? AIBridge.GetOtherBridgeEnd(source) : AITunnel.GetOtherTunnelEnd(source);
if (AIMap.IsValidTile(endat)) { source = endat }
}
dirswap.AddItem(source+AIMap.GetTileIndex(0, 1), 0);
dirswap.AddItem(source+AIMap.GetTileIndex(1, 0), 0);
dirswap.AddItem(source+AIMap.GetTileIndex(-1, 0), 0);
dirswap.AddItem(source+AIMap.GetTileIndex(0, -1), 0);
dirswap.Valuate(Distance, target);
dirswap.Sort(AIList.SORT_BY_VALUE, AIList.SORT_ASCENDING);
foreach (direction, voisin in dirswap)
{
if (road_type==AIVehicle.VT_ROAD) { valid = AIRoad.AreRoadTilesConnected(source, direction); }
if (road_type==AIVehicle.VT_RAIL) { valid = cBuilder.AreRailTilesConnected(source, direction); }
if (road_type==AIVehicle.VT_WATER) { valid = (AITile.IsWaterTile(direction) || AIMarine.IsDockTile(direction)); }
if (road_type==666) { valid = (AITile.IsBuildable(direction) || AITile.IsWaterTile(direction)); }
local currdistance = Distance(direction, target);
if (currdistance > origin+max_wrong_direction) { valid=false; }
if (walkedtiles.Count() > 5 * origin) { valid = false; }
if (walkedtiles.HasItem(direction)) { valid=false; }
if (valid) { walkedtiles.AddItem(direction,0); }
//if (valid && INSTANCE.debug) cDebug.PutSign(direction, currdistance);
//if (INSTANCE.debug) DInfo("Valid="+valid+" curdist="+currdistance+" origindist="+origin+" source="+source+" dir="+direction+" target="+target,2);
if (!found && valid) { solve = INSTANCE.main.builder.RoadRunnerHelper(direction, target, road_type, walkedtiles, origin); }
if (!found) { found = !solve.IsEmpty(); }
if (found) { solve.AddItem(source,walkedtiles.Count()); return solve; }
}
return solve;
}
function cBuilder::RoadRunner(source, target, road_type, distance = null)
{
cDebug.ClearSigns();
cDebug.PutSign(source, "S");
cDebug.PutSign(target, "T");
local solve = cBuilder.RoadRunnerHelper(source, target, road_type);
cDebug.ClearSigns();
local result = !solve.IsEmpty();
if (result && distance != null && solve.Count() > distance*2) { result=false; }
cDebug.ClearSigns();
//cDebug.showLogic(solve);
return result;
}
function cBuilder::BuildRoadROAD(head1, head2, stationID)
// Pathfind and building the road.
// AllowDelay to false to get immediate road construction and block script until its end.
// AllowDelay to true to use cPathfinder class that makes all pathfinding advance, but let the script continue others tasks.
// we return true or false if it fail
{
print("in BuildRoadROAD stationID="+stationID);
while (true)
{
local result = cPathfinder.GetStatus(head1, head2, stationID);
if (result == -1) { cError.RaiseError(); cPathfinder.CloseTask(head1, head2); return false; }
if (result == 2) { cPathfinder.CloseTask(head1, head2); return true; }
cPathfinder.AdvanceAllTasks();
}
print("out BuildRoadROAD");
}
function cBuilder::AsyncConstructRoadROAD(src, dst, stationID)
// this construct (build) the road we get from path
{
local status=cPathfinder.GetStatus(src, dst, stationID);
local path=cPathfinder.GetSolve(src, dst);
local smallerror = 0;
if (path == null) { smallerror = -2; }
switch (status)
{
case 0: // still pathfinding
return 0;
case -1: // failure
return -1;
case 2: // succeed
return 2;
case 3: // waiting child to end
return 0;
}
// 1 is non covered as it's end of pathfinding, and should call us to build
DInfo("Building road structure",0);
local prev = null;
local prevprev = null;
local prevprevprev = null;
local counter=0;
local walked=[];
cBanker.RaiseFundsBigTime();
while (path != null)
{
local par = path.GetParent();
if (par != null)
{
if (AIMap.DistanceManhattan(path.GetTile(), par.GetTile()) == 1)
{
if (!AIRoad.BuildRoad(path.GetTile(), par.GetTile()))
{
smallerror=cBuilder.EasyError(AIError.GetLastError());
if (smallerror==-1)
{
DInfo("An error occured while I was building the road: " + AIError.GetLastErrorString(),2);
return false;
}
if (smallerror==-2) { break; }
}
else
{
cTileTools.BlackListTile(par.GetTile(), -stationID);
}
} // aimap
else
{
if (!cBridge.IsBridgeTile(path.GetTile()) && !AITunnel.IsTunnelTile(path.GetTile()))
{
if (AIRoad.IsRoadTile(path.GetTile())) { cTileTools.DemolishTile(path.GetTile()); }
if (AITunnel.GetOtherTunnelEnd(path.GetTile()) == par.GetTile())
{
if (!AITunnel.BuildTunnel(AIVehicle.VT_ROAD, path.GetTile()))
{
smallerror=cBuilder.EasyError(AIError.GetLastError());
if (smallerror==-1)
{
DInfo("An error occured while I was building the tunnel: " + AIError.GetLastErrorString(),2);
return false;
}
if (smallerror==-2) { break; }
}
else
{
cTileTools.BlackListTile(par.GetTile(), -stationID);
}
} // aitunnel
else
{
local bridgeID = cBridge.GetCheapBridgeID(AIVehicle.VT_ROAD, AIMap.DistanceManhattan(path.GetTile(), par.GetTile()) + 1);
if (!AIBridge.BuildBridge(AIVehicle.VT_ROAD, bridgeID, path.GetTile(), par.GetTile()))
{
smallerror=cBuilder.EasyError(AIError.GetLastError());
if (smallerror==-1)
{
DInfo("An error occured while I was building the bridge: " + AIError.GetLastErrorString(),2);
return false;
}
if (smallerror==-2) { break; }
}
else
{
cTileTools.BlackListTile(par.GetTile(), -stationID);
}
}//else ai tunnel
}//if cBrigde
}// else aimap
} //ifpar
prev = path;
path = par;
}
local mytask=cPathfinder.GetPathfinderObject(cPathfinder.GetUID(src, dst));
local source=cPathfinder.GetUID(mytask.r_source, mytask.r_target);
if (smallerror == -2)
{
DError("Pathfinder has detect a failure.",1);
if (walked.len() < 4)
{
DInfo("Pathfinder cannot do more",1);
// unroll all tasks and fail
source=cPathfinder.GetUID(mytask.source, mytask.target);
while (source != null)
{
// remove all sub-tasks
DInfo("Pathfinder helper task "+source+" failure !",1);
source=cPathfinder.GetUID(mytask.r_source, mytask.r_target);
if (source != null)
{
cPathfinder.CloseTask(mytask.source, mytask.target);
mytask=cPathfinder.GetPathfinderObject(source);
}
}
DInfo("Pathfinder task "+mytask.UID+" failure !",1);
mytask.status=-1;
local badtiles=AIList();
badtiles.AddList(cTileTools.TilesBlackList); // keep blacklisted tiles for -stationID
badtiles.KeepValue(-mytask.stationID);
foreach (tiles, dummy in badtiles) cTileTools.UnBlackListTile(tiles); // and release them for others
cError.RaiseError();
return false;
}
else
{
local maxstepback=10;
local curr=walked.pop(); // dismiss last one, it's the failure
if (walked.len() < maxstepback) { maxstepback=walked.len()-1; }
local alist=AIList();
for (local ii=1; ii < maxstepback; ii++)
{
prev=walked.pop();
alist.AddItem(prev, 0);
AIRoad.RemoveRoad(prev, curr);
curr=prev;
}
local newtarget=[0, prev];
DInfo("Pathfinder is calling an helper task",1);
// Create the helper task
local dummy= cPathfinder.GetStatus(src, newtarget, stationID);
dummy=cPathfinder.GetPathfinderObject(cPathfinder.GetUID(src, newtarget));
dummy.r_source=cPathfinder.GetSourceX(src);
dummy.r_target=cPathfinder.GetTargetX(newtarget);
mytask.status=3; // wait for subtask end
return false;
}
}
else // we cannot get smallerror==-1 because on -1 it always return, so limit to 0 or -2
{
// let's see if we success or an helper task has succeed for us
if (source != null)
{
source=cPathfinder.GetUID(mytask.source, mytask.target);
while (source != null)
{
// remove all sub-tasks
DInfo("Pathfinder helper task "+source+" succeed !",1);
source=cPathfinder.GetUID(mytask.r_source, mytask.r_target);
if (source != null)
{
//DInfo("Pathfinder helper task "+source+" succeed !",1);
cPathfinder.CloseTask(mytask.source, mytask.target);
mytask=cPathfinder.GetPathfinderObject(source);
}
}
}
DInfo("Pathfinder task "+mytask.UID+" succeed !",1);
mytask.status=2;
INSTANCE.buildDelay = 0;
local bltiles=AIList();
bltiles.AddList(cTileTools.TilesBlackList);
bltiles.KeepValue(-stationID);
local sta = cStation.Load(stationID);
if (cMisc.ValidInstance(sta)) { sta.s_TilesOther.AddList(bltiles); }
return true;
}
}
DictatorAI/road/truckbuilder.nut 0000644 0001750 0000144 00000007212 12203021373 016250 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cCarrier::GetRoadVehicle(routeidx, cargoID = -1)
// return the vehicle we will pickup if we try to build a vehicle on that route
{
local object = cEngineLib.Infos();
object.cargo_id = cargoID;
object.engine_type = AIVehicle.VT_ROAD;
object.engine_roadtype = AIRoad.ROADTYPE_ROAD;
if (cargoID == -1)
{
local road=cRoute.Load(routeidx);
if (!road) return -1;
object.cargo_id = road.CargoID;
object.depot = cRoute.GetDepot(routeidx);
}
local veh = cEngineLib.GetBestEngine(object, cCarrier.VehicleFilterRoad);
return veh[0];
}
function cCarrier::CreateRoadVehicle(roadidx)
// Build a road vehicle for route roadidx
// return true/false
{
if (!INSTANCE.use_road) return false;
local road=cRoute.Load(roadidx);
if (!road) return false;
local engineID = cCarrier.GetRoadVehicle(roadidx);
if (engineID == -1) { DWarn("Cannot find any road vehicle to transport that cargo "+cCargo.GetCargoLabel(road.CargoID),1); return false; }
local homedepot = road.SourceStation.s_Depot;//cRoute.GetDepot(roadidx);
local srcplace = road.SourceStation.s_Location;
local dstplace = road.TargetStation.s_Location;
local altplace=(road.Twoway && road.VehicleCount > 0 && road.VehicleCount % 2 != 0);
if (altplace) { homedepot = road.TargetStation.s_Depot; }
if (!cStation.IsDepot(homedepot))
{
homedepot = cRoute.GetDepot(roadidx);
if (!cStation.IsDepot(homedepot)) { return false; }
}
local price=cEngine.GetPrice(engineID, road.CargoID);
cBanker.RaiseFundsBy(price);
local vehID = cEngineLib.CreateVehicle(homedepot, engineID, road.CargoID);
if (vehID != -1)
{
DInfo("Just brought a new road vehicle: "+cCarrier.GetVehicleName(vehID),0);
INSTANCE.main.carrier.vehicle_cash -= price;
}
else {
DError("Cannot create the road vehicle "+cEngine.GetName(engineID),2);
return false;
}
local firstorderflag = AIOrder.OF_NON_STOP_INTERMEDIATE + AIOrder.OF_FULL_LOAD_ANY;
local secondorderflag = AIOrder.OF_NON_STOP_INTERMEDIATE;
if (road.Twoway) { firstorderflag = secondorderflag; }
else { secondorderflag += AIOrder.OF_NO_LOAD; }
AIGroup.MoveVehicle(road.GroupID, vehID);
if (!AIOrder.AppendOrder(vehID, srcplace, firstorderflag))
{ // detect weirdness, like IsArticulated bug, maybe remove it as openttd fix it, but keeping it for newGRF trouble
DInfo("Vehicle "+cCarrier.GetVehicleName(vehID)+" refuse order !",1);
local checkstation = AIStation.GetStationID(srcplace);
local checkengine = AIVehicle.GetEngineType(vehID);
local checktype = AIEngine.GetVehicleType(checkengine);
if (AIStation.IsValidStation(checkstation) && (AIStation.HasStationType(checkstation, AIStation.STATION_BUS_STOP) || AIStation.HasStationType(checkstation, AIStation.STATION_TRUCK_STOP)))
{
cEngine.BlacklistEngine(checkengine);
cCarrier.VehicleSell(vehID, false);
}
return false;
}
AIOrder.AppendOrder(vehID, dstplace, secondorderflag);
if (altplace) { INSTANCE.main.carrier.VehicleOrderSkipCurrent(vehID); }
road.VehicleCount++;
if (!cCarrier.StartVehicle(vehID)) { DError("Cannot start the vehicle:",2); cCarrier.VehicleSell(vehID, false); return false; }
cEngine.CheckMaxSpeed(engineID);
return true;
}
DictatorAI/train/ 0000755 0001750 0000144 00000000000 12203764642 013224 5 ustar krinn users DictatorAI/train/railbuilder.nut 0000755 0001750 0000144 00000120331 12203764640 016253 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cBuilder::BuildTrainStation(start)
// It's where we find a spot for our train station
// Unlike classic stations that need best spot where to get cargo, train stations best spot
// is the one that can do its task while still provide most space to futher allow station upgrading
{
// for industry: to grab goods: best is near where it produce and closest to target
// to drop goods: best is farest target while still accept goods
// train station best direction is | when left or right the industry
// and -- when up or down the industry location, this offer best upgrade chance
// for town, saddly it's contrary, station is best -- when left/right and | when up/down
// because that configuration will almost always cut entry or exit point of station, but offer higher
// chance to enlarge the station without going too much into the city
local dir, tilelist, otherplace = null;
local rad = AIStation.GetCoverageRadius(AIStation.STATION_TRAIN);
local istown=false;
local srcpoint=null;
local sourceplace=null;
local statile=null;
if (start)
{
dir = INSTANCE.main.builder.GetDirection(INSTANCE.main.route.SourceProcess.Location, INSTANCE.main.route.TargetProcess.Location);
if (INSTANCE.main.route.SourceProcess.IsTown)
{
tilelist = cTileTools.GetTilesAroundTown(INSTANCE.main.route.SourceProcess.ID);
tilelist.Valuate(AITile.IsBuildable);
tilelist.KeepValue(1);
tilelist.Valuate(AITile.GetCargoProduction, INSTANCE.main.route.CargoID, 1, 1, rad);
tilelist.KeepAboveValue(0);
tilelist.Sort(AIList.SORT_BY_VALUE, AIList.SORT_DESCENDING);
otherplace=INSTANCE.main.route.TargetProcess.Location; sourceplace=INSTANCE.main.route.SourceProcess.Location;
istown=true;
}
else
{
tilelist = AITileList_IndustryProducing(INSTANCE.main.route.SourceProcess.ID, rad);
tilelist.Valuate(AITile.IsBuildable);
tilelist.KeepValue(1);
istown=false;
otherplace=INSTANCE.main.route.TargetProcess.Location; sourceplace=INSTANCE.main.route.SourceProcess.Location;
tilelist.Valuate(AIMap.DistanceSquare, otherplace);
tilelist.Sort(AIList.SORT_BY_VALUE, AIList.SORT_ASCENDING);
}
}
else
{
dir = INSTANCE.main.builder.GetDirection(INSTANCE.main.route.TargetProcess.Location, INSTANCE.main.route.SourceProcess.Location);
if (INSTANCE.main.route.TargetProcess.IsTown)
{
tilelist = cTileTools.GetTilesAroundTown(INSTANCE.main.route.TargetProcess.ID);
tilelist.Valuate(AITile.IsBuildable);
tilelist.KeepValue(1);
tilelist.Valuate(AITile.GetCargoAcceptance, INSTANCE.main.route.CargoID, 1, 1, rad);
tilelist.KeepAboveValue(8);
tilelist.Sort(AIList.SORT_BY_VALUE, AIList.SORT_DESCENDING);
otherplace=INSTANCE.main.route.SourceProcess.Location; sourceplace=INSTANCE.main.route.TargetProcess.Location;
istown=true;
}
else
{
tilelist = AITileList_IndustryAccepting(INSTANCE.main.route.TargetProcess.ID, rad);
tilelist.Valuate(AITile.IsBuildable);
tilelist.KeepValue(1);
otherplace=INSTANCE.main.route.SourceProcess.Location; sourceplace=INSTANCE.main.route.TargetProcess.Location;
tilelist.Valuate(AIMap.DistanceSquare, otherplace);
tilelist.Sort(AIList.SORT_BY_VALUE, AIList.SORT_ASCENDING);
istown=false;
}
}
local success = false;
local buildmode=0;
local cost=5*AIRail.GetBuildCost(AIRail.GetCurrentRailType(),AIRail.BT_STATION);
DInfo("Rail station cost: "+cost+" byinflat"+(cost*cBanker.GetInflationRate()),2);
INSTANCE.main.bank.RaiseFundsBy(cost*4);
local ssize=6+INSTANCE.main.carrier.train_length;
/* 3 build mode:
- try find a place with stationsize+11 tiles flatten and buildable
- try find a place with stationsize+11 tiles maybe not flat and buildable
- try find a place with stationsize+11 tiles maybe not flat and buildable even on water
*/
do
{
foreach (tile, _ in tilelist)
{
cDebug.PutSign(tile, buildmode);
if (start) { dir = cBuilder.GetDirection(tile, INSTANCE.main.route.SourceProcess.Location); }
else { dir = cBuilder.GetDirection(tile, INSTANCE.main.route.TargetProcess.Location); }
switch (dir)
{
case DIR_NW: //0 south
if (istown) { dir=AIRail.RAILTRACK_NW_SE; }
else { dir=AIRail.RAILTRACK_NE_SW; }
break;
case DIR_SE: //1 north
if (istown) { dir=AIRail.RAILTRACK_NW_SE; }
else { dir=AIRail.RAILTRACK_NE_SW; }
break;
case DIR_SW: //3 est/droite
if (istown) { dir=AIRail.RAILTRACK_NE_SW; }
else { dir=AIRail.RAILTRACK_NW_SE; }
break;
case DIR_NE: //2 west/gauche
if (istown) { dir=AIRail.RAILTRACK_NE_SW; }
else { dir=AIRail.RAILTRACK_NW_SE; }
break;
}
local checkit = false;
switch (buildmode)
{
case 0:
if (dir == AIRail.RAILTRACK_NW_SE) { checkit = cTileTools.IsBuildableRectangleFlat(tile, 2, ssize); }
else { checkit = cTileTools.IsBuildableRectangleFlat(tile, ssize, 2); }
if (checkit) { checkit = tile; }
break;
case 1:
if (dir == AIRail.RAILTRACK_NW_SE) { checkit = AITile.IsBuildableRectangle(tile, 2, ssize); }
else { checkit = AITile.IsBuildableRectangle(tile, ssize, 2); }
if (checkit) { checkit = tile; }
break;
case 2:
if (dir == AIRail.RAILTRACK_NW_SE) { checkit = cTileTools.CheckLandForConstruction(tile, 2, ssize); }
else { checkit = cTileTools.CheckLandForConstruction(tile, ssize, 2); }
break;
}
if (checkit != false)
{
success = cBuilder.CreateAndBuildTrainStation(checkit, dir);
if (!success && cError.IsCriticalError()) { break; }
statile = tile;
break;
}
}
buildmode++;
}
while (buildmode != 4 && !success);
if (!success)
{
DInfo("Can't find a good place to build the train station ! "+tilelist.Count(),1);
if (tilelist.IsEmpty()) { cError.RaiseError(); }
return false;
}
// here, so we success to build one
local staID = AIStation.GetStationID(statile);
if (start) { INSTANCE.main.route.SourceStation = staID; }
else { INSTANCE.main.route.TargetStation = staID; }
INSTANCE.main.route.CreateNewStation(start);
return true;
}
function cBuilder::EasyError(error)
// Just return if the error is something really simple we could handle with few time or bucks
// 0 no error
// -1 a temp easy solvable error
// -2 a big error
{
switch (error)
{
case AIError.ERR_NOT_ENOUGH_CASH :
return -1;
case AIError.ERR_ALREADY_BUILT:
return 0;
case AIError.ERR_VEHICLE_IN_THE_WAY:
return -1;
case AIError.ERR_OWNED_BY_ANOTHER_COMPANY:
return 0;
}
return -2;
}
function cBuilder::BuildRoadRAIL(head1, head2, useEntry, stationID)
{
local status=cPathfinder.GetStatus(head1, head2, stationID, useEntry);
local path=cPathfinder.GetSolve(head1, head2);
local smallerror=0;
if (path == null) { smallerror=-2; }
switch (status)
{
case 0: // still pathfinding
return 0;
case -1: // failure
return -1;
case 2: // succeed
return 2;
case 3: // waiting child to end
return 0;
}
// 1 is non covered as it's end of pathfinding, and should call us to build
cBanker.RaiseFundsBigTime();
local prev = null;
local prevprev = null;
local pp1, pp2, pp3 = null;
local walked=[];
cTrack.SetRailType(AIRail.GetRailType(AIStation.GetLocation(stationID)));
while (path != null && smallerror==0)
{
if (prevprev != null)
{
if (AIMap.DistanceManhattan(prev, path.GetTile()) > 1)
{
if (AITunnel.GetOtherTunnelEnd(prev) == path.GetTile())
{
if (!AITunnel.BuildTunnel(AIVehicle.VT_RAIL, prev))
{
DInfo("An error occured while I was building the rail: " + AIError.GetLastErrorString(),2);
smallerror=cBuilder.EasyError(AIError.GetLastError());
if (smallerror==-1)
{
DInfo("That tunnel would be too expensive. Construction aborted.",2);
return false;
}
if (smallerror==-2) { break; }
}
else
{
cTileTools.BlackListTile(prev, 0 -(1000+stationID));
// i mark them as blacklist and assign to -stationID, so i could recover them later
cTileTools.BlackListTile(path.GetTile(), 0 - (1000+stationID));
}
}
else
{
local bridgeID = cBridge.GetCheapBridgeID(AIVehicle.VT_RAIL, AIMap.DistanceManhattan(path.GetTile(), prev) + 1);
if (!AIBridge.BuildBridge(AIVehicle.VT_RAIL, bridgeID, prev, path.GetTile()))
{
DInfo("An error occured while I was building the rail: " + AIError.GetLastErrorString(),2);
smallerror=cBuilder.EasyError(AIError.GetLastError());
if (smallerror==-1)
{
DInfo("That bridge would be too expensive. Construction aborted.",2);
return false;
}
if (smallerror==-2) { break; }
}
else
{
cTileTools.BlackListTile(prev, 0 - (1000+stationID));
cTileTools.BlackListTile(path.GetTile(), 0 -(1000+stationID));
}
cBridge.IsBridgeTile(prev); // force bridge check
}
pp3 = pp2;
pp2 = pp1;
pp1 = prevprev;
prevprev = prev;
prev = path.GetTile();
path = path.GetParent();
walked.push(prev);
}
else
{
local targetTile=path.GetTile();
cTileTools.DemolishTile(targetTile); // try cleanup the place before
if (!AIRail.BuildRail(prevprev, prev, targetTile))
{
smallerror=cBuilder.EasyError(AIError.GetLastError());
if (smallerror==-1)
{
DInfo("An error occured while I was building the rail: " + AIError.GetLastErrorString(),2);
return false;
}
if (smallerror==-2) { break; }
}
else
{
cTileTools.BlackListTile(prev, 0 - (1000+stationID));
cTileTools.BlackListTile(path.GetTile(), 0 -(1000+stationID));
}
}
}
if (path != null)
{
pp3 = pp2;
pp2 = pp1;
pp1 = prevprev;
prevprev = prev;
prev = path.GetTile();
path = path.GetParent();
walked.push(prev);
}
}
local mytask=cPathfinder.GetPathfinderObject(cPathfinder.GetUID(head1, head2));
local source=cPathfinder.GetUID(mytask.r_source, mytask.r_target);
if (smallerror == -2)
{
DError("Pathfinder has detect a failure.",1);
if (walked.len() < 4)
{
DInfo("Pathfinder cannot do more",1);
// unroll all tasks and fail
source=cPathfinder.GetUID(mytask.source, mytask.target);
while (source != null)
{
// remove all sub-tasks
DInfo("Pathfinder helper task "+source+" failure !",1);
source=cPathfinder.GetUID(mytask.r_source, mytask.r_target);
if (source != null)
{
cPathfinder.CloseTask(mytask.source, mytask.target);
mytask=cPathfinder.GetPathfinderObject(source);
}
}
DInfo("Pathfinder task "+mytask.UID+" failure !",1);
mytask.status = -1;
local badtiles = AIList();
badtiles.AddList(cTileTools.TilesBlackList); // keep blacklisted tiles for -stationID
badtiles.KeepValue(0 - (1000+ mytask.stationID));
cTrack.RailCleaner(badtiles); // remove all rail we've built
cTileTools.TilesBlackList.RemoveList(badtiles); // and release them for others
cError.RaiseError();
return false;
}
else
{
local maxstepback=10;
walked.pop(); // dismiss last one, it's the failure
if (walked.len() < maxstepback) { maxstepback=walked.len()-1; }
local alist=AIList();
for (local ii=1; ii < maxstepback; ii++)
{
prev=walked.pop();
alist.AddItem(prev, 0);
}
prevprev=walked.pop();
cTrack.RailCleaner(alist);
cTileTools.TilesBlackList.RemoveList(alist);
local newtarget=[prev, prevprev];
DInfo("Pathfinder is calling an helper task",1);
// Create the helper task
local dummy= cPathfinder.GetStatus(head1, newtarget, stationID, useEntry);
dummy=cPathfinder.GetPathfinderObject(cPathfinder.GetUID(head1, newtarget));
dummy.r_source=head1;
dummy.r_target=head2;
mytask.status=3; // wait for subtask end
return false;
}
}
else // we cannot get smallerror==-1 because on -1 it always return, so limit to 0 or -2
{
// let's see if we success or an helper task has succeed for us
if (source != null)
{
source=cPathfinder.GetUID(mytask.source, mytask.target);
while (source != null)
{
// remove all sub-tasks
DInfo("Pathfinder helper task "+source+" succeed !",1);
source=cPathfinder.GetUID(mytask.r_source, mytask.r_target);
if (source != null)
{
cPathfinder.CloseTask(mytask.source, mytask.target);
mytask=cPathfinder.GetPathfinderObject(source);
}
}
}
DInfo("Pathfinder task "+mytask.UID+" succeed !",1);
local verifypath = RailFollower.GetRailPathing(mytask.source, mytask.target);
//src_target, src_link, dst_target, dst_link);
if (verifypath.IsEmpty())
{
mytask.status = -1;
DError("Pathfinder task "+mytask.UID+" fails when checking the path.",1);
local badtiles=AIList();
badtiles.AddList(cTileTools.TilesBlackList); // keep blacklisted tiles for -stationID
badtiles.KeepValue(0 - (1000 + mytask.stationID));
cTrack.RailCleaner(badtiles); // remove all rail we've built
cTileTools.TilesBlackList.RemoveList(badtiles); // and release them for others
cError.RaiseError();
return false;
}
else
{
DInfo("Pathfinder task "+mytask.UID+" pass checks.",1);
mytask.status=2;
INSTANCE.buildDelay=0;
local bltiles=AIList();
bltiles.AddList(cTileTools.TilesBlackList);
bltiles.KeepValue(0-(1000+stationID));
cStation.StationClaimTile(bltiles, stationID, useEntry); // assign tiles to that station
local staobj = cStation.Load(stationID);
if (!staobj) { cError.RaiseError(); return false; }
if (cStationRail.IsPrimaryLineBuilt(stationID) && !cStationRail.IsAlternateLineBuilt(stationID))
{
local uid_obj = cRoute.Load(staobj.s_Train[TrainType.OWNER]);
if (!uid_obj) { return false; }
cBuilder.RailStationPathfindAltTrack(uid_obj);
}
return true;
}
}
}
function cBuilder::FindStationEntryToExitPoint(src, dst)
// find the closest path from station src to station dst
// We return result in array: 0=src tile, 1=1(entry)0(exit), 1=dst tile, value=1(entry)0(exit)
// return array empty on error
//
{
// check entry/exit avaiablility on stations
local srcEntry=cStationRail.IsRailStationEntryOpen(src);
local srcExit=cStationRail.IsRailStationExitOpen(src);
local dstEntry=cStationRail.IsRailStationEntryOpen(dst);
local dstExit=cStationRail.IsRailStationExitOpen(dst);
local frontTile = cStationRail.GetRelativeTileForward(src, true);
local srcEntryLoc = (5*frontTile) + cStationRail.GetRailStationFrontTile(true, cStation.GetLocation(src), src);
frontTile = cStationRail.GetRelativeTileForward(dst, true);
local dstEntryLoc = (5*frontTile) + cStationRail.GetRailStationFrontTile(true, cStation.GetLocation(dst), dst);
frontTile = cStationRail.GetRelativeTileForward(src, false);
local srcExitLoc = (5*frontTile) + cStationRail.GetRailStationFrontTile(false, cStation.GetLocation(src), src);
frontTile = cStationRail.GetRelativeTileForward(dst, false);
local dstExitLoc = (5*frontTile) + cStationRail.GetRailStationFrontTile(false, cStation.GetLocation(dst), dst);
local srcStation=cStation.Load(src);
local dstStation=cStation.Load(dst);
if ( !srcStation || !dstStation || (!srcEntry && !srcExit) || (!dstEntry && !dstExit) )
{
DInfo("That station have its entry and exit closed. No more connections could be made with it",1);
return [];
}
local srcEntryBuild=(srcStation.s_EntrySide[TrainSide.IN] != -1); // because if we have build it and it is still open, we must use that point, even if it's not the shortest path
local srcExitBuild=(srcStation.s_ExitSide[TrainSide.IN] != -1); // else we may prefer another point that would be shorter, leaving that entry/exit built for nothing
local dstEntryBuild=(dstStation.s_EntrySide[TrainSide.OUT] != -1);
local dstExitBuild=(dstStation.s_ExitSide[TrainSide.OUT] != -1);
local best=100000000000;
local bestsrc=-1;
local bestdst=-1;
local check=-1;
local srcFlag=0;
local dstFlag=0; // use to check if we're connect to entry(1) or exit(0)
if (srcEntry && !srcExitBuild)
{
if (dstExit)
{
check = AIMap.DistanceManhattan(srcEntryLoc,dstExitLoc);
if (check < best && !dstEntryBuild) { best=check; bestsrc=srcEntryLoc; bestdst=dstExitLoc; }
}
DInfo("distance="+check+" bestsrc="+bestsrc+" bestdst="+bestdst,2);
if (dstEntry)
{
check = AIMap.DistanceManhattan(srcEntryLoc,dstEntryLoc);
if (check < best && !dstExitBuild) { best=check; bestsrc=srcEntryLoc; bestdst=dstEntryLoc; }
}
DInfo("distance="+check+" bestsrc="+bestsrc+" bestdst="+bestdst,2);
}
if (srcExit && !srcEntryBuild)
{
if (dstEntry)
{
check = AIMap.DistanceManhattan(srcExitLoc,dstEntryLoc);
if (check < best && !dstExitBuild) { best=check; bestsrc=srcExitLoc; bestdst=dstEntryLoc; }
}
DInfo("distance="+check+" bestsrc="+bestsrc+" bestdst="+bestdst,2);
if (dstExit)
{
check = AIMap.DistanceManhattan(srcExitLoc,dstExitLoc);
if (check < best && !dstEntryBuild) { best=check; bestsrc=srcExitLoc; bestdst=dstExitLoc; }
}
DInfo("distance="+check+" bestsrc="+bestsrc+" bestdst="+bestdst,2);
}
// Now we know where to build our roads
local bestWay=[];
if (check == -1) { return []; }
if (bestsrc == srcEntryLoc) { srcFlag=1; }
if (bestdst == dstEntryLoc) { dstFlag=1; }
bestWay.push(bestsrc);
bestWay.push(srcFlag);
bestWay.push(bestdst);
bestWay.push(dstFlag);
DInfo("Best connecting source="+bestsrc+" destination="+bestdst+" srcFlag="+srcFlag+" dstFlag="+dstFlag,2);
cDebug.PutSign(bestsrc,"CS");
cDebug.PutSign(bestdst,"CT");
return bestWay;
}
function cBuilder::CreateStationsConnection(fromObj, toObj)
// Connect station fromObj to station toObj by picking entry/exit close to each other and create connections in front of them
// fromObj, toObj: 2 valid rail stations
// this also set the INSTANCE.main.route.* properties
{
local srcStation=cStation.Load(fromObj);
local dstStation=cStation.Load(toObj);
if (!srcStation || !dstStation) { return -1; }
DInfo("Connecting rail station "+srcStation.s_Name+" to "+dstStation.s_Name,1);
if (cStationRail.IsPrimaryLineBuilt(fromObj) && cStationRail.IsPrimaryLineBuilt(toObj))
{
if (!cStationRail.IsAlternateLineBuilt(fromOjb) || !cStationRail.IsAlternateLineBuilt(toObj)) return false;
return true;
}
local retry=true;
local bestWay=AIList();
local srcresult=false;
local dstresult=false;
local srcpos=null;
local dstpos=null;
local srclink=0;
local dstlink=0;
local srcUseEntry=null;
local dstUseEntry=null;
do
{
bestWay=INSTANCE.main.builder.FindStationEntryToExitPoint(fromObj, toObj);
if (bestWay.len()==0) { cError.RaiseError(); return false; }
else { retry=true; }
if (retry) // we found a possible connection
{
srcpos=bestWay[0];
dstpos=bestWay[2];
srcUseEntry=(bestWay[1]==1);
dstUseEntry=(bestWay[3]==1);
DInfo("srcUseEntry="+srcUseEntry+" dstUseEntry="+dstUseEntry,2);
if (!srcresult) { srcresult=INSTANCE.main.builder.RailStationGrow(fromObj, srcUseEntry, true); }
if (!srcresult)
{
DWarn("RailStationGrow report failure",1);
if (cError.IsError()) { return false; }
}
if (!dstresult) { dstresult=INSTANCE.main.builder.RailStationGrow(toObj, dstUseEntry, false); }
if (!dstresult)
{
DWarn("RailStationGrow report failure",1);
if (cError.IsError()) { return false; }
}
if (dstresult && srcresult)
{
// need to grab the real locations first, as they might have change while building entrances of station
local mainowner=srcStation.s_Train[TrainType.OWNER];
if (srcUseEntry) { srclink=srcStation.s_EntrySide[TrainSide.IN_LINK]; }
else { srclink=srcStation.s_ExitSide[TrainSide.IN_LINK]; }
if (dstUseEntry) { dstlink=dstStation.s_EntrySide[TrainSide.OUT_LINK]; }
else { dstlink=dstStation.s_ExitSide[TrainSide.OUT_LINK]; }
srcpos=srclink+cStationRail.GetRelativeTileBackward(srcStation.s_ID, srcUseEntry);
dstpos=dstlink+cStationRail.GetRelativeTileBackward(dstStation.s_ID, dstUseEntry);
DInfo("Calling rail pathfinder: srcpos="+srcpos+" dstpos="+dstpos+" srclink="+srclink+" dstlink="+dstlink,2);
local result=cPathfinder.GetStatus([srclink,srcpos],[dstlink,dstpos], srcStation.s_ID, srcUseEntry);
switch (result)
{
case -1:
cPathfinder.CloseTask([srclink,srcpos],[dstlink,dstpos]);
cError.RaiseError();
return false;
break;
case 2:
retry = false;
break;
default:
return false;
}
dstStation.s_Train[TrainType.OWNER]= INSTANCE.main.route.UID;
srcStation.s_Train[TrainType.OWNER]= INSTANCE.main.route.UID;
}
}
}
while (retry);
// pfff here, all connections were made, and rails built
INSTANCE.main.route.Source_RailEntry=srcUseEntry;
INSTANCE.main.route.Target_RailEntry=dstUseEntry;
INSTANCE.main.route.SourceStation.SetPrimaryLineBuilt();
INSTANCE.main.route.TargetStation.SetPrimaryLineBuilt();
INSTANCE.main.route.Primary_RailLink=true;
INSTANCE.main.route.Route_GroupNameSave();
cPathfinder.CloseTask([srclink,srcpos],[dstlink,dstpos]);
return true;
}
function cBuilder::CreateAndBuildTrainStation(tilepos, direction, link = AIStation.STATION_NEW)
// Create a new station, we still don't know if station will be usable
// that's a task handle by CreateStationConnection
// link: true to link to a previous station
{
local c = AITile.GetTownAuthority(tilepos);
if (AITown.IsValidTown(c) && AITown.GetRating(c, AICompany.COMPANY_SELF) < AITown.TOWN_RATING_POOR) { cTileTools.SeduceTown(c); }
local money = INSTANCE.main.carrier.train_length*AIRail.GetBuildCost(AIRail.GetCurrentRailType(), AIRail.BT_STATION)*cBanker.GetInflationRate();
if (!cBanker.CanBuyThat(money)) { DInfo("We lack money to buy the station",1); }
cBanker.RaiseFundsBy(money);
if (link != AIStation.STATION_NEW && AIStation.IsValidStation(link))
{
local rt = AIRail.GetRailType(AIStation.GetLocation(link));
cTrack.SetRailType(rt);
}
if (!AIRail.BuildRailStation(tilepos, direction, 1, INSTANCE.main.carrier.train_length, link))
{
DInfo("Rail station couldn't be built, link="+link+" cost="+money+" err: "+AIError.GetLastErrorString(),1);
cDebug.PutSign(tilepos,"!");
return false;
}
return true;
}
function cBuilder::RailStationRemovePlatform(staloc)
// remove a rail station platform we found at staloc
// discard any errors, we just try to remove it
{
local tilelist=cTileTools.FindStationTiles(statile);
if (tilelist.IsEmpty()) { return true; }
local Keeper=null;
local railtrack=null;
if (AIRail.GetRailStationDirection(staloc) == AIRail.RAILTRACK_NW_SE)
{ tilelist.Valuate(AIMap.GetTileX); Keeper=AIMap.GetTileX(staloc); railtrack=AIRail.RAILTRACK_NW_SE; }
else { tilelist.Valuate(AIMap.GetTileY); Keeper=AIMap.GetTileY(staloc); railtrack=AIRail.RAILTRACK_NE_SW; }
tilelist.KeepValue(Keeper);
cDebug.showLogic(tilelist);
foreach (tile, dummy in tilelist) AIRail.RemoveRailTrack(tile, railtrack);
return true;
}
function cBuilder::PlatformConnectors(platform, useEntry)
// connect a platform (build rail and the signal before crosspoint)
// platform: platform tile to work on
// useEntry: connect the platform entry or exit
// on error -1, if rails are already there, no error is report, only if we cannot manage to connect it
{
local frontTile=cStationRail.GetPlatformFrontTile(platform, useEntry);
if (frontTile==-1) { DError("Invalid front tile",1); return false; }
local stationID=AIStation.GetStationID(platform);
local thatstation=cStation.Load(stationID);
if (!thatstation) { return false; }
cTrack.SetRailType(thatstation.s_SubType); // not to forget
local crossing = 0;
if (useEntry) { crossing=thatstation.s_EntrySide[TrainSide.CROSSING]; }
else { crossing=thatstation.s_ExitSide[TrainSide.CROSSING]; }
if (crossing < 0) { DError("Crossing isn't define yet",2); return false; }
local forwardTileOf=cStationRail.GetRelativeTileForward(stationID, useEntry);
local backwardTileOf=cStationRail.GetRelativeTileBackward(stationID, useEntry);
local leftTileOf=cStationRail.GetRelativeTileLeft(stationID, useEntry);
local rightTileOf=cStationRail.GetRelativeTileRight(stationID, useEntry);
local direction=thatstation.GetRailStationDirection();
local goal=0;
local railFront, railCross, railLeft, railRight, railUpLeft, railUpRight = null;
if (direction == AIRail.RAILTRACK_NW_SE)
{
railFront=AIRail.RAILTRACK_NW_SE;
railCross=AIRail.RAILTRACK_NE_SW;
if (useEntry) // going NW->SE
{
railLeft=AIRail.RAILTRACK_SW_SE;
railRight=AIRail.RAILTRACK_NE_SE;
railUpLeft=AIRail.RAILTRACK_NW_SW;
railUpRight=AIRail.RAILTRACK_NW_NE;
}
else // going SE->NW
{
railLeft=AIRail.RAILTRACK_NW_NE;
railRight=AIRail.RAILTRACK_NW_SW;
railUpLeft=AIRail.RAILTRACK_NE_SE;
railUpRight=AIRail.RAILTRACK_SW_SE;
}
goal=AIMap.GetTileIndex(AIMap.GetTileX(frontTile),AIMap.GetTileY(crossing));
}
else // NE_SW
{
railFront=AIRail.RAILTRACK_NE_SW;
railCross=AIRail.RAILTRACK_NW_SE;
if (useEntry) // going NE->SW
{
railLeft=AIRail.RAILTRACK_NW_SW;
railRight=AIRail.RAILTRACK_SW_SE;
railUpLeft=AIRail.RAILTRACK_NW_NE;
railUpRight=AIRail.RAILTRACK_NE_SE;
}
else // going SW->NE
{
railLeft=AIRail.RAILTRACK_NE_SE;
railRight=AIRail.RAILTRACK_NW_NE;
railUpLeft=AIRail.RAILTRACK_SW_SE;
railUpRight=AIRail.RAILTRACK_NW_SW;
}
goal=AIMap.GetTileIndex(AIMap.GetTileX(crossing),AIMap.GetTileY(frontTile));
}
local rail=railFront;
local sweeper=AIList();
local error=false;
cDebug.PutSign(goal,"g");
cTileTools.TerraformLevelTiles(frontTile+backwardTileOf, goal);
local i=frontTile;
local signaldone=false;
while (i != goal)
{
cTileTools.DemolishTile(i); // rails are protect there
cDebug.PutSign(i,"o");
if (cTileTools.CanUseTile(i, thatstation.s_ID) && cTrack.DropRailHere(rail, i, stationID, useEntry))
{
sweeper.AddItem(i, 0);
signaldone = (AIRail.GetSignalType(i, i+backwardTileOf) != AIRail.SIGNALTYPE_NONE);
if (!signaldone) { signaldone= cError.ForceAction(AIRail.BuildSignal, i, i+backwardTileOf, AIRail.SIGNALTYPE_PBS); }
}
else
{
error = cTileTools.CanUseTile(i, thatstation.s_ID);
if (error) { break; }
}
i+=forwardTileOf;
}
if (!error)
{
local sta_left = frontTile+backwardTileOf+leftTileOf;
local sta_right = frontTile+backwardTileOf+rightTileOf;
local got_left = (AIStation.GetStationID(sta_left) == stationID);
local got_right = (AIStation.GetStationID(sta_right) == stationID);
cTileTools.DemolishTile(goal);
if (got_left) { cTrack.DropRailHere(railLeft, goal, stationID, useEntry); }
if (got_right) { cTrack.DropRailHere(railRight, goal, stationID, useEntry); }
if (got_left && got_right) { cTrack.DropRailHere(railCross, goal, stationID, useEntry); }
cBuilder.RailConnectorSolver(goal+backwardTileOf, goal, true);
}
if (error) { cTrack.RailCleaner(sweeper, stationID); }
return true;
}
function cBuilder::GetRailBitMask(rails)
// Return a nibble bitmask with each NE,SW,NW,SE direction set to 1
{
local NE = 1; // we will use them as bitmask
local SW = 2;
local NW = 4;
local SE = 8;
local trackMap=AIList();
trackMap.AddItem(AIRail.RAILTRACK_NE_SW, NE + SW); // AIRail.RAILTRACK_NE_SW
trackMap.AddItem(AIRail.RAILTRACK_NW_SE, NW + SE); // AIRail.RAILTRACK_NW_SE
trackMap.AddItem(AIRail.RAILTRACK_NW_NE, NW + NE); // AIRail.RAILTRACK_NW_NE
trackMap.AddItem(AIRail.RAILTRACK_SW_SE, SW + SE); // AIRail.RAILTRACK_SW_SE
trackMap.AddItem(AIRail.RAILTRACK_NW_SW, NW + SW); // AIRail.RAILTRACK_NW_SW
trackMap.AddItem(AIRail.RAILTRACK_NE_SE, NE + SE); // AIRail.RAILTRACK_NE_SE
if (rails==255) { return 0; } // invalid rail
local railmask=0;
foreach (tracks, value in trackMap)
{
if ((rails & tracks)==tracks) { railmask=railmask | value; }
if (railmask==(NE+SW+NW+SE)) { return railmask; } // no need to test more tracks
}
return railmask;
}
function cBuilder::AreRailTilesConnected(tilefrom, tileto, stricttype=true)
// Look at tilefront and build rails to connect that tile to its neightbourg tiles that are us with rails
// tilefrom, tileto : tiles to check
// return true if you can walk from tilefrom to tileto
// stricttype : true to only allow walking same railtype, false allow walking on different railtype
{
local atemp=AICompany.ResolveCompanyID(AICompany.COMPANY_SELF);
if (AITile.GetOwner(tilefrom) != atemp) { return false; } // not own by us
if (AITile.GetOwner(tileto) != atemp) { return false; } // not own by us
atemp=AIRail.GetRailType(tilefrom);
if (AIRail.GetRailType(tileto) != atemp && stricttype) { return false; } // not same railtype
local NE = 1; // we will use them as bitmask
local SW = 2;
local NW = 4;
local SE = 8;
local direction=cBuilder.GetDirection(tilefrom, tileto);
local tilefrom_mask=cBuilder.GetRailBitMask(AIRail.GetRailTracks(tilefrom));
local tileto_mask=cBuilder.GetRailBitMask(AIRail.GetRailTracks(tileto));
local tilefrom_need, tileto_need=0;
switch (direction)
{
case 0: // SE-NW, it's easy, if we want go SE->N
tilefrom_need=NW;
tileto_need=SE;
break;
case 1: // NW-SE
tilefrom_need=SE;
tileto_need=NW;
break;
case 2: // SW-NE
tilefrom_need=NE;
tileto_need=SW;
break;
case 3: // NE-SW
tilefrom_need=SW;
tileto_need=NE;
break;
}
if (AIRail.IsRailDepotTile(tileto) && AIRail.GetRailDepotFrontTile(tileto)==tilefrom) { tileto_mask=tileto_need; }
if (AIRail.IsRailDepotTile(tilefrom) && AIRail.GetRailDepotFrontTile(tilefrom)==tileto) { tilefrom_mask=tilefrom_need; }
// if we have a depot, make it act like it is a classic rail if its entry match where we going or come from
if (cBridge.IsBridgeTile(tileto) || AITunnel.IsTunnelTile(tileto))
{
local endat=null;
endat=cBridge.IsBridgeTile(tileto) ? AIBridge.GetOtherBridgeEnd(tileto) : AITunnel.GetOtherTunnelEnd(tileto);
local jumpdir=cBuilder.GetDirection(tileto, endat);
if (jumpdir == direction) // if the bridge/tunnel goes the same direction, then consider it a plain rail
{
tileto_mask=tileto_need;
}
}
if (cBridge.IsBridgeTile(tilefrom) || AITunnel.IsTunnelTile(tilefrom))
{
local endat=null;
endat=cBridge.IsBridgeTile(tilefrom) ? AIBridge.GetOtherBridgeEnd(tilefrom) : AITunnel.GetOtherTunnelEnd(tilefrom);
local jumpdir=cBuilder.GetDirection(endat, tilefrom); // reverse direction to find the proper one
if (jumpdir == direction) // if the bridge/tunnel goes the same direction, then consider it a plain rail
{
tilefrom_mask=tilefrom_need;
}
}
if ( (tilefrom_mask & tilefrom_need)==tilefrom_need && (tileto_mask & tileto_need)==tileto_need) { return true; }
return false;
}
function cBuilder::GetRailTracks(tile)
// Return the rail tracks as AIRail.GetRailTracks, except it convert depot, tunnel and bridge to a railtrack
{
local trackinfo = AIRail.GetRailTracks(tile);
if (trackinfo==255)
{
// maybe a tunnel, depot or bridge that "could" also be valid entries
local testdir=null;
local test = null;
if (AIRail.IsRailDepotTile(tile))
{
test=AIRail.GetRailDepotFrontTile(tile);
testdir=cBuilder.GetDirection(tile, test);
DInfo("Rail depot found",2);
trackinfo= (testdir == 0 || testdir == 1) ? AIRail.RAILTRACK_NW_SE : AIRail.RAILTRACK_NE_SW;
}
if (AITunnel.IsTunnelTile(tile))
{
test=AITunnel.GetOtherTunnelEnd(tile);
testdir=cBuilder.GetDirection(tile, test);
DInfo("Tunnel found",2);
trackinfo = (testdir == 0 || testdir == 1) ? AIRail.RAILTRACK_NW_SE : AIRail.RAILTRACK_NE_SW;
}
if (cBridge.IsBridgeTile(tile))
{
test=AIBridge.GetOtherBridgeEnd(tile);
testdir=cBuilder.GetDirection(tile, test);
DInfo("Bridge found",2);
trackinfo = (testdir == 0 || testdir == 1) ? AIRail.RAILTRACK_NW_SE : AIRail.RAILTRACK_NE_SW;
}
}
return trackinfo;
}
function cBuilder::RailConnectorSolver(tile_link, tile_target, everything=true)
// Look at tile_target and build rails to connect that tile to tile_link
// Tiles must be direct neighbor (distance = 1) and same rail type (or no rails at tile_target)
// tile_link : the tile to connect tile_target with
// tile_target : tile we will work on (build rails)
// everything: if true we will connect tile_target to all its neighbors and not only to tile_link
// only return false if we fail to build all tracks need at tile_target (and raise critical error)
{
if (AITile.GetDistanceManhattanToTile(tile_link,tile_target) != 1)
{ DError("We must use two tiles close to each other ! tile_link="+tile_link+" tile_target="+tile_target,1); return false; }
local track_orig = cBuilder.GetRailTracks(tile_link);
if (track_orig == 255) { DWarn("No tracks found at "+tile_link,1); return false; }
local track_dest = cBuilder.GetRailTracks(tile_target);
local voisin=[AIMap.GetTileIndex(0,1), AIMap.GetTileIndex(0,-1), AIMap.GetTileIndex(1,0), AIMap.GetTileIndex(-1,0)]; // SE, NW, SW, NE
local NE = 1; // we will use them as bitmask
local SW = 2;
local NW = 4;
local SE = 8;
local WorkTiles = AIList();
local type_orig = AIRail.GetRailType(tile_link);
local track_dest = cBuilder.GetRailTracks(tile_target);
if (track_dest != 255 && AIRail.GetRailType(tile_target) != type_orig)
{ DWarn("Cannot connect rail tiles because railtype aren't the same",1); return false; }
local z = 3;
for (local i=0; i < 4; i++)
{
if (tile_target+voisin[i] == tile_link) { continue; } // we will add it later, see allTiles()
if (AIRail.GetRailType(tile_target+voisin[i]) != type_orig) { continue;}
if (!AICompany.IsMine(AITile.GetOwner(tile_target+voisin[i]))) { continue; }
WorkTiles.AddItem(tile_target+voisin[i],z);
z++;
}
local trackMap = AIList(); // item=two points added, value=the track need to link 1st point with 2nd point
local edges = [];
edges.push(NE); edges.push(SW); edges.push(NW); edges.push(SE);
local tracks = [];
trackMap.AddItem(NE + SW, AIRail.RAILTRACK_NE_SW);
trackMap.AddItem(NW + SE, AIRail.RAILTRACK_NW_SE);
trackMap.AddItem(NW + NE, AIRail.RAILTRACK_SW_SE);
trackMap.AddItem(SW + SE, AIRail.RAILTRACK_NW_NE);
trackMap.AddItem(NW + SW, AIRail.RAILTRACK_NE_SE);
trackMap.AddItem(NE + SE, AIRail.RAILTRACK_NW_SW);
local directionmap= AIList();
directionmap.AddItem(0, SE); // for SE->NW
directionmap.AddItem(1, NW); // for NW->SE
directionmap.AddItem(2, SW); // for SW->NE
directionmap.AddItem(3, NE); // for NE->SW
local addtrack = [];
local seek_search = null;
local mask_seek = null;
local mask_voisin = null;
local allTiles = AIList();
local filter = AIList();
allTiles.AddItem(tile_link,2); // add the link tile
for (local i = 2; i < 6; i++) filter.AddItem(i*i, 0); // filter duplicates
if (everything) {
foreach (tile, index in WorkTiles)
{
allTiles.AddItem(tile, index); // would have been easier to tile*tile but squirrel overflow fast
}
}
foreach (tile_seek, seek_index in allTiles)
{
mask_seek = cBuilder.GetRailBitMask(cBuilder.GetRailTracks(tile_seek));
foreach (dest, dest_index in WorkTiles)
{
if (filter.HasItem(seek_index*dest_index)) { continue; }
else { filter.AddItem(seek_index*dest_index,0); }
mask_voisin = cBuilder.GetRailBitMask(cBuilder.GetRailTracks(dest));
seek_search = directionmap.GetValue(cBuilder.GetDirection(tile_target, tile_seek));
// we ask direction target->seek to find what point tile_seek need set, so SW-SE = SW
local dest_search = directionmap.GetValue(cBuilder.GetDirection(tile_target, dest));
if ( (dest_search & mask_voisin) == dest_search && (seek_search & mask_seek) == seek_search )
{
addtrack.push(dest_search + seek_search);
}
} //foreach WorkTiles
} // foreach allTiles
if (addtrack.len()>0)
foreach (pair in addtrack)
{
local track = trackMap.GetValue(pair);
if (!cTrack.DropRailHere(track, tile_target))
{
cError.IsCriticalError();
if (cError.IsError()) { return false; }
}
}
return true;
}
function cBuilder::SignalBuilder(source, target)
// Follow all directions to walk through the path starting at source, ending at target
// return true if we build all signals
{
local max_signals_distance=3;
local spacecounter=0;
local signdir=0;
local railpath=AIList();
local directions=[AIMap.GetTileIndex(0, -1), AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(-1, 0), AIMap.GetTileIndex(1, 0)];
local dir=null;
local sourcedir=null;
local targetdir=null;
cDebug.PutSign(source,"S");
cDebug.PutSign(target,"T");
local sourcecheck=null;
local targetcheck=null;
foreach (voisin in directions)
{
if (AIRail.GetSignalType(source, source+voisin) == AIRail.SIGNALTYPE_PBS)
{
sourcedir=cBuilder.GetDirection(source+voisin, source);
DInfo("Found source signal at "+source+" facing "+sourcedir+" voisin="+(source+voisin),2);
sourcecheck=source+voisin; // to feed pathfinder with a tile without the signal on it
cDebug.PutSign(sourcecheck,"s");
}
}
if (sourcedir == null) { DError("Cannot find source signal at "+source,2); return false; }
foreach (voisin in directions)
{
if (AIRail.GetSignalType(target, target+voisin) == AIRail.SIGNALTYPE_PBS)
{
targetdir=cBuilder.GetDirection(target+voisin, target);
DInfo("Found target signal at "+target+" facing "+targetdir+" voisin="+(target+voisin),2);
targetcheck=target+voisin;
cDebug.PutSign(targetcheck,"t");
}
}
if (targetdir == null) { DError("Cannot find target signal at "+target,2); return false; }
local pathwalker = RailFollower();
pathwalker.InitializePath([[source, sourcecheck]], [[targetcheck, target]]);// start beforestart end afterend
local path = pathwalker.FindPath(20000);
if (path == null) { DError("Pathwalking failure.",2); return false; }
local cc=0;
local prev = path.GetTile();
local allsuccess=true;
local tilesource, tilefront = null;
while (path != null)
{
local tile = path.GetTile();
switch (targetdir) // target cause the path is record from target->source
{
case 0: // SE-NW
tilesource=prev;
tilefront=tile;
break;
case 1: // NW-SE
tilesource=prev;//*
tilefront=tile;
break;
case 2: // SW-NE
tilesource=prev;
tilefront=tile;
break;
case 3: // NE-SW //*
tilesource=prev;
tilefront=tile;
break;
}
if (cc >= max_signals_distance)
{
local ignoreit=false;
if (AIRail.GetSignalType(tilesource,tilefront) != AIRail.SIGNALTYPE_NONE) { ignoreit=true; }
if (cBridge.IsBridgeTile(tilesource) || AITunnel.IsTunnelTile(tilesource)) { ignoreit=true; }
if (ignoreit) { cc=0; prev=tile; continue; }
if (AIRail.BuildSignal(tilesource,tilefront, AIRail.SIGNALTYPE_NORMAL)) { cc=0; }
else
{
cDebug.PutSign(tile,"!");
local smallerror=cBuilder.EasyError(AIError.GetLastError());
DError("Error building signal ",1);
//max_signals_distance++;
if (smallerror == -1) { return false; }
}
}
AIController.Sleep(1);
cc++;
prev=tile;
path = path.GetParent();
}
return allsuccess;
}
DictatorAI/train/trainbuilder.nut 0000644 0001750 0000144 00000031532 12203442546 016440 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cCarrier::ChooseRailCouple(cargo, rtype = -1, depot = -1, forengine = -1)
// This function will choose a wagon to carry that cargo, and a train engine to pull the wagon
// It will return an array : [0] = train_engineID, [1] = wagon_engineID [2] the railtype need for that couple
// empty array
{
local object = cEngineLib.Infos();
object.bypass = !DictatorAI.GetSetting("use_nicetrain");
object.cargo_id = cargo;
object.depot = depot;
object.engine_type = AIVehicle.VT_RAIL;
object.engine_roadtype = rtype;
object.engine_id = forengine;
cBanker.RaiseFundsBigTime();
local veh = cEngineLib.GetBestEngine(object, cCarrier.VehicleFilterTrain);
if (veh[0] != -1)
{
DInfo("selected train couple : "+cEngine.GetName(veh[0])+" to pull "+cEngine.GetName(veh[1])+" for "+cCargo.GetCargoLabel(cargo)+" using railtype "+veh[2],1);
}
else
{
DInfo("ChooseRailCouple return error "+AIError.GetLastErrorString()+" * "+cEngineLib.GetAPIError(),2);
}
return veh;
}
function cCarrier::GetWagonsInGroup(groupID)
// return number of wagons present in the group
{
if (groupID == null) { return 0; }
local vehlist=AIVehicleList_Group(groupID);
local total=0;
foreach (veh, dummy in vehlist) total+=cEngineLib.GetNumberOfWagons(veh);
return total;
}
function cCarrier::ForceAddTrain(uid, wagons)
// Force adding a train to the route without checks
{
local road = cRoute.Load(uid);
if (!road) { return -1; }
if (road.Status != RouteStatus.WORKING) { return -1; }
local depot = cRoute.GetDepot(uid);
if (depot == -1) { return -1; }
local maxlength = road.SourceStation.s_Train[TrainType.DEPTH]*16;
local tID, t_wagon;
while (wagons > 0)
{
DInfo("Force building a train with "+wagons+" wagons",1);
tID = cCarrier.AddNewTrain(uid, null, wagons, depot, maxlength);
if (!AIVehicle.IsValidVehicle(tID)) { return -1; }
AIGroup.MoveVehicle(road.GroupID, tID);
cRoute.AddTrain(uid, tID);
t_wagon = cEngineLib.RestrictLength_Vehicle(tID, maxlength);
if (t_wagon > 0) { cTrain.SetFull(tID, true); }
t_wagon = cEngineLib.GetNumberOfWagons(tID);
if (t_wagon == 0)
{
cCarrier.VehicleSell(tID, false);
return -1;
}
cCarrier.TrainExitDepot(tID);
cTrain.SetDepotVisit(tID);
wagons -= t_wagon;
}
}
function cCarrier::AddNewTrain(uid, trainID, wagonNeed, depot, maxLength)
// Add a train or add wagons to an existing train
{
local road=cRoute.Load(uid);
if (!road) { return -1; }
local locotype = -1;
local wagontype = -1;
if (trainID == null)
{
locotype= cCarrier.ChooseRailCouple(road.CargoID, road.SourceStation.s_SubType, depot, -1);
if (locotype[0] == -1) { return -1; }
else { wagontype = locotype[1]; locotype = locotype[0]; }
}
else { locotype=AIVehicle.GetEngineType(trainID); }
if (wagontype == -1)
{
wagontype= cCarrier.ChooseRailCouple(road.CargoID, road.SourceStation.s_SubType, -1, locotype);
if (wagontype[0] == -1) { return -1; }
wagontype = wagontype[1];
}
local wagonID = null;
local pullerID = null;
if (trainID == null) {
pullerID = cEngineLib.CreateVehicle(depot, locotype, road.CargoID);
if (pullerID == -1) { INSTANCE.main.carrier.vehicle_cash += cEngineLib.GetPrice(locotype, road.CargoID); }
else {
INSTANCE.main.carrier.vehicle_cash -= cEngineLib.GetPrice(locotype, road.CargoID);
INSTANCE.main.carrier.highcostTrain = 0;
}
}
else { pullerID = trainID; }
if (pullerID == -1) {
DError("Cannot create the train engine "+AIEngine.GetName(locotype),1);
INSTANCE.main.carrier.highcostTrain = cEngineLib.GetPrice(locotype, road.CargoID);
return -1;
}
local freightlimit = cCargo.IsFreight(road.CargoID);
local beforesize = cEngineLib.GetNumberOfWagons(pullerID);
if (freightlimit > -1 && beforesize+wagonNeed > freightlimit && !cEngineLib.IsMultiEngine(pullerID) && !cTrain.IsFull(pullerID))
{
local xengine = cCarrier.ChooseRailCouple(road.CargoID, road.SourceStation.s_SubType, depot, wagontype);
if (xengine[0] == -1) { xengine = -1; }
else { xengine = xengine[0]; }
if (xengine != -1) { xengine = cEngineLib.CreateVehicle(depot, xengine, road.CargoID); }
if (xengine == -1) { DInfo("Cannot add an extra engine to that train, will redo later",1); }
else {
if (!AIVehicle.MoveWagon(xengine, 0, pullerID, AIVehicle.GetNumWagons(pullerID) - 1))
{ AIVehicle.SellVehicle(xengine); }
else { DInfo("Added an extra engine to pull freight",1); }
}
}
for (local i=0; i < wagonNeed; i++)
{
local nwagonID = cEngineLib.CreateVehicle(depot, wagontype, road.CargoID);
if (nwagonID != -1)
{
if (!AIVehicle.MoveWagonChain(nwagonID, 0, pullerID, AIVehicle.GetNumWagons(pullerID)-1))
{
DError("Wagon "+AIEngine.GetName(wagontype)+" cannot be attach to "+AIEngine.GetName(locotype),2);
AIVehicle.SellVehicle(nwagonID);
INSTANCE.main.carrier.vehicle_cash -= cEngineLib.GetPrice(wagontype, road.CargoID);
break;
}
else
{
if (AIVehicle.GetLength(pullerID) > maxLength)
{
// prevent building too much wagons for nothing and prevent failure that let wagons remain in the depot forever
DInfo("Stopping adding wagons to train as its length is already too big",2);
break;
}
}
}
}
return pullerID;
}
function cCarrier::AddWagon(uid, wagonNeed)
// Add wagons to route uid, handle the train engine by buying it if need
// If need we send a train to depot to get more wagons, and we will be called back by the in depot event
// We will return true if we consider the job done (adding wagons)
// This is not always without getting an error, but we will says true per example if we cannot add new wagons because the station cannot support more wagons...
{
local road=cRoute.Load(uid);
if (!road) { return false; }
local vehlist = AIVehicleList_Station(road.SourceStation.s_ID);
foreach (veh, dummy in vehlist)
{
if (cCarrier.ToDepotList.HasItem(veh) && cCarrier.VehicleSendToDepot_GetReason(cCarrier.ToDepotList.GetValue(veh)) == DepotAction.SIGNALUPGRADE)
{
DInfo("Cannot do any action with trains while we're upgrading signals",1);
return true;
}
}
if (wagonNeed == 0) { return true; }
vehlist=AIVehicleList_Group(road.GroupID);
local indepot = [];
local canorder = AIList();
local stationLen=road.SourceStation.s_Train[TrainType.DEPTH]*16;
local processTrains=[];
local tID=null;
local depotID=cRoute.GetDepot(uid);
cDebug.PutSign(depotID,"Depot Builder");
local giveup=false;
foreach (veh, dummy in vehlist)
{
local state = AIVehicle.GetState(veh);
if (state != AIVehicle.VS_IN_DEPOT && state != AIVehicle.VS_RUNNING) { continue; }
if (state == AIVehicle.VS_IN_DEPOT) { indepot.push(veh); continue; } //wait handling
if (cCarrier.ToDepotList.HasItem(veh) && cCarrier.VehicleSendToDepot_GetReason(cCarrier.ToDepotList.GetValue(veh)) == DepotAction.ADDWAGON)
{
DInfo("Updating number of wagons need for "+cCarrier.GetVehicleName(veh)+" to "+wagonNeed,1);
cCarrier.ToDepotList.SetValue(veh, DepotAction.ADDWAGON+wagonNeed);
return true;
}
if (!cTrain.IsFull(veh)) { canorder.AddItem(veh, 0); } // can be called
}
if (indepot.len() != 0) { processTrains.extend(indepot); }
else
{
local before = canorder.IsEmpty();
canorder.Valuate(cTrain.CanModifyTrain);
canorder.KeepValue(1);
if (!before && canorder.IsEmpty()) { return true; } // no one can be called
if (canorder.IsEmpty())
{
DInfo("No train can hold "+wagonNeed+" new wagons, forcing creation of a new train",2);
processTrains.push(-1);
}
else
{
if (road.MoreTrain == 1)
{
DInfo("Not calling that train until MoreTrain query is done",1);
return false;
}
canorder.Valuate(AIVehicle.GetNumWagons);
canorder.Sort(AIList.SORT_BY_VALUE, AIList.SORT_ASCENDING);
local veh = canorder.Begin();
local wagonID = AIVehicle.GetWagonEngineType(veh, 1);
local wagonprice = 1000;
if (AIEngine.IsValidEngine(wagonID)) { wagonprice = AIEngine.GetPrice(wagonID); }
wagonprice = wagonNeed * wagonprice;
if (!cBanker.CanBuyThat(wagonprice)) { return false; } // don't call it if we lack money
DInfo("Sending a train to depot to add "+wagonNeed+" more wagons",1);
cCarrier.VehicleSendToDepot(veh, DepotAction.ADDWAGON+wagonNeed);
return true; // calling one, give up
}
}
local numTrains = vehlist.Count();
do
{
tID=processTrains.pop();
if (tID == -1)
{
// build a new engine loco
if (!cRoute.CanAddTrainToStation(uid))
{
DInfo("Cannot add any trains anymore as one of the station cannot handle one more",1);
return true;
}
DInfo("Adding a new engine to create a train",0);
depotID=cRoute.GetDepot(uid); // because previous CanAddTrainToStation could have move it
local stop=false;
while (!stop)
{
stop=true;
tID=cCarrier.AddNewTrain(uid, null, 0, depotID, stationLen);
if (AIVehicle.IsValidVehicle(tID))
{
numTrains++;
AIGroup.MoveVehicle(road.GroupID, tID);
local topspeed=AIEngine.GetMaxSpeed(AIVehicle.GetEngineType(tID));
cRoute.AddTrain(uid, tID);
if (INSTANCE.main.carrier.speed_MaxTrain < topspeed)
{
DInfo("Setting maximum speed for trains to "+topspeed+"km/h",0);
INSTANCE.main.carrier.speed_MaxTrain=topspeed;
}
}
else
{
if (tID == -2) { stop=false; } // loop so we pickup another loco engine
else { giveup=true; }
}
}
}
if (AIVehicle.IsValidVehicle(tID) && !giveup)
{
print(AIVehicle.GetName(tID)+" here for "+wagonNeed+" wagons");
// now we can add wagons to it
local beforesize = cEngineLib.GetNumberOfWagons(tID);
depotID=AIVehicle.GetLocation(tID);
local freightlimit=cCargo.IsFreight(road.CargoID);
if (road.MoreTrain == 3 || numTrains > 1 || beforesize+wagonNeed < 5)
{ tID=cCarrier.AddNewTrain(uid, tID, wagonNeed, depotID, stationLen); print("add wagons"); road.MoreTrain = 3; }
else
{
print("new train");
if (road.MoreTrain == 0) { road.MoreTrain = 1; }
local couple = [];
couple.push(AIVehicle.GetEngineType(tID));
couple.push(AIVehicle.GetWagonEngineType(tID, cEngineLib.GetWagonFromVehicle(tID)));
local newwagon = cEngineLib.RestrictLength_Wagons(couple, stationLen);
if (newwagon == -1) newwagon = 0;
if (wagonNeed - newwagon <= 0) newwagon = 0;
tID=cCarrier.AddNewTrain(uid, tID, newwagon, depotID, stationLen);
processTrains.push(-1);
DInfo("Adding "+newwagon+" wagons to this train and create another one with "+(wagonNeed-newwagon),1);
}
if (AIVehicle.IsValidVehicle(tID))
{
local newwagon = cEngineLib.GetNumberOfWagons(tID)-beforesize;
wagonNeed -= newwagon;
if (wagonNeed <= 0) { wagonNeed=0; }
local res = cEngineLib.RestrictLength_Vehicle(tID, stationLen);
if (res != -1) { wagonNeed += res; cTrain.SetFull(tID, true); }
if (cEngineLib.GetNumberOfWagons(tID) == 0)
{
DInfo("Train have no wagons... selling it",2);
cCarrier.VehicleSell(tID, false);
}
else
{
cCarrier.Lower_VehicleWish(road.GroupID, newwagon - wagonNeed);
cCarrier.TrainExitDepot(tID);
cTrain.SetDepotVisit(tID);
}
}
}
}
while (processTrains.len()!=0 && !giveup);
road.RouteUpdateVehicle();
return !giveup;
}
DictatorAI/train/stationchecker.nut 0000644 0001750 0000144 00000076130 12203741744 016767 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cBuilder::RailStationPhaseGrowing(stationObj, newStationSize, useEntry)
{
DInfo("--- Phase 1: grow",1);
DInfo("Upgrading "+stationObj.s_Name+" to "+newStationSize+" platforms",1);
cDebug.ClearSigns();
local allfail=false;
local topLeftPlatform=stationObj.s_Train[TrainType.PLATFORM_LEFT];
local topRightPlatform=stationObj.s_Train[TrainType.PLATFORM_RIGHT];
local idxRightPlatform=cStationRail.GetPlatformIndex(topRightPlatform, useEntry);
local idxLeftPlatform=cStationRail.GetPlatformIndex(topLeftPlatform, useEntry);
local leftTileOf=cStationRail.GetRelativeTileLeft(stationObj.s_ID, useEntry);
local rightTileOf=cStationRail.GetRelativeTileRight(stationObj.s_ID, useEntry);
local forwardTileOf=cStationRail.GetRelativeTileForward(stationObj.s_ID, useEntry);
local backwardTileOf=cStationRail.GetRelativeTileBackward(stationObj.s_ID, useEntry);
local station_depth = stationObj.s_Train[TrainType.DEPTH];
local direction = stationObj.GetRailStationDirection();
local station_left = null;
local station_right = null;
local station_right = cBuilder.GetDirection(cStationRail.GetPlatformIndex(topRightPlatform, false), cStationRail.GetPlatformIndex(topRightPlatform, true));
local station_left=cTileTools.GetLeftRelativeFromDirection(station_right);
station_right=cTileTools.GetRightRelativeFromDirection(station_right);
cDebug.PutSign(station_left+idxLeftPlatform,"LS");
cDebug.PutSign(station_right+idxRightPlatform,"RS");
// default: main station + exit in use = best place to build a platform : left side
local plat_main=idxLeftPlatform;
local plat_alt=idxRightPlatform;
local pside=station_left;
local platopenclose=cMisc.CheckBit(stationObj.s_Platforms.GetValue(topLeftPlatform), 1);
if (useEntry) // main station + entry in use = best place : right side
{
pside=station_right;
plat_main=idxRightPlatform;
plat_alt=idxLeftPlatform;
platopenclose=cMisc.CheckBit(stationObj.s_Platforms.GetValue(topRightPlatform), 0);
}
local success = false;
local displace=plat_main+pside;
local areaclean = AITileList();
if (platopenclose)
{
areaclean.AddRectangle(displace,displace+(backwardTileOf*(station_depth-1)));
local canDestroy=cTileTools.IsAreaBuildable(areaclean);
cDebug.showLogic(areaclean); // deb
if (canDestroy) { cTileTools.ClearArea(areaclean); }
cTileTools.TerraformLevelTiles(plat_main, displace+(backwardTileOf*(station_depth-1)));
success=INSTANCE.main.builder.CreateAndBuildTrainStation(cStationRail.GetPlatformIndex(plat_main,true)+pside, direction, stationObj.s_ID);
cDebug.PutSign(cStationRail.GetPlatformIndex(plat_main,true)+pside,"+");
if (success) { foreach (tile, dummy in areaclean) stationObj.StationClaimTile(tile, stationObj.s_ID); }
}
if (!success)
{
cError.IsCriticalError();
allfail=cError.IsError();
cError.ClearError();
pside=station_right;
if (useEntry) { pside=station_left; }
displace=plat_alt+pside;
local areaclean=AITileList();
areaclean.AddRectangle(displace,displace+(backwardTileOf*(station_depth-1)));
cDebug.showLogic(areaclean);
if (cTileTools.IsAreaBuildable(areaclean)) { cTileTools.ClearArea(areaclean); }
cTileTools.TerraformLevelTiles(plat_alt, displace+(backwardTileOf*(station_depth-1)));
success=INSTANCE.main.builder.CreateAndBuildTrainStation(cStationRail.GetPlatformIndex(plat_alt,true)+pside, direction, stationObj.s_ID);
cDebug.PutSign(cStationRail.GetPlatformIndex(plat_alt,true)+pside,"+");
if (success) { foreach (tile, dummy in areaclean) stationObj.StationClaimTile(tile, stationObj.s_ID); }
if (!success)
{
cError.IsCriticalError();
if (cError.IsError() && allfail)
{
// We will never be able to build one more station platform in that station so
DInfo("Critical failure, station couldn't be upgrade anymore!",1);
stationObj.s_MaxSize=stationObj.s_Size;
cError.RaiseError(); // Make sure caller will be aware of that failure
return false;
}
else
{
DInfo("Temporary failure, station couldn't be upgrade for now",1);
return false;
}
}
}
// if we are here, we endup successfuly add a new platform to the station
return true;
}
function cBuilder::RailStationPhaseDefineCrossing(stationObj, useEntry)
{
DInfo("--- Phase2: define entry/exit point",1);
local leftTileOf=cStationRail.GetRelativeTileLeft(stationObj.s_ID, useEntry);
local rightTileOf=cStationRail.GetRelativeTileRight(stationObj.s_ID, useEntry);
local forwardTileOf=cStationRail.GetRelativeTileForward(stationObj.s_ID, useEntry);
local backwardTileOf=cStationRail.GetRelativeTileBackward(stationObj.s_ID, useEntry);
local towncheck=AITileList();
local testcheck=AITileList();
local workTile = stationObj.GetRailStationFrontTile(useEntry, stationObj.GetLocation());
towncheck.AddRectangle(workTile, workTile+rightTileOf+(5*forwardTileOf));
testcheck.AddList(towncheck);
local success=false;
if (cTileTools.IsAreaBuildable(towncheck))
{
testcheck.AddList(towncheck);
testcheck.Valuate(AITile.IsStationTile); // protect station here
testcheck.KeepValue(1);
if (testcheck.IsEmpty())
{
// now look if we're not going too much in a town
local neartown=AITile.GetClosestTown(workTile);
local s_dst=AITown.GetDistanceManhattanToTile(neartown,workTile);
local s_end=AITown.GetDistanceManhattanToTile(neartown,workTile+(4*forwardTileOf));
if (s_dst < 10 && s_end < 10 && s_dst > s_end) // we must be going farer inside the town
{
DInfo("Giving up, we're probably going inside "+AITown.GetName(neartown),1);
success=false;
}
else { success=true; }
}
else { success=false; } // station there
}
else // not everything is removable, still we might success to cross a road
{
testcheck.Valuate(AIRoad.IsRoadTile);
testcheck.KeepValue(0);
success=(testcheck.IsEmpty());
}
if (success) { foreach (tile, dummy in towncheck) cTileTools.DemolishTile(tile); }
else { DInfo("We gave up, too much troubles",1); return false; }
DInfo("--- Phase3: define crossing point",1);
local direction=stationObj.GetRailStationDirection();
local railCross, railFront, railLeft, railRight, railUpLeft, railUpRight, rail = null;
if (direction == AIRail.RAILTRACK_NW_SE)
{
railFront=AIRail.RAILTRACK_NW_SE;
railCross=AIRail.RAILTRACK_NE_SW;
if (useEntry) // going NW->SE
{
railLeft=AIRail.RAILTRACK_SW_SE;
railRight=AIRail.RAILTRACK_NE_SE;
railUpLeft=AIRail.RAILTRACK_NW_SW;
railUpRight=AIRail.RAILTRACK_NW_NE;
}
else // going SE->NW
{
railLeft=AIRail.RAILTRACK_NW_NE;
railRight=AIRail.RAILTRACK_NW_SW;
railUpLeft=AIRail.RAILTRACK_NE_SE;
railUpRight=AIRail.RAILTRACK_SW_SE;
}
}
else // NE_SW
{
railFront=AIRail.RAILTRACK_NE_SW;
railCross=AIRail.RAILTRACK_NW_SE;
if (useEntry) // going NE->SW
{
railLeft=AIRail.RAILTRACK_NW_SW;
railRight=AIRail.RAILTRACK_SW_SE;
railUpLeft=AIRail.RAILTRACK_NW_NE;
railUpRight=AIRail.RAILTRACK_NE_SE;
}
else // going SW->NE
{
railLeft=AIRail.RAILTRACK_NE_SE;
railRight=AIRail.RAILTRACK_NW_NE;
railUpLeft=AIRail.RAILTRACK_SW_SE;
railUpRight=AIRail.RAILTRACK_NW_SW;
}
}
rail=railLeft;
DInfo("Building crossing point ",2);
local j=1; local temptile=null; local crossing = -1;
local se_crossing = -1;
local sx_crossing = -1;
local position = stationObj.GetLocation();
cTileTools.DemolishTile(workTile);
if (!AITile.IsBuildable(workTile)) { return false; } // because we must have a tile in front of the station buildable for the signal
do {
temptile=workTile+(j*forwardTileOf);
cTileTools.TerraformLevelTiles(position,temptile);
if (cTileTools.CanUseTile(temptile,stationObj.s_ID))
{
cTileTools.DemolishTile(temptile);
success = cTrack.DropRailHere(rail, temptile, stationObj.s_ID, useEntry);
}
else { return false; }
if (success)
{
if (useEntry) { se_crossing=temptile; crossing=se_crossing; }
else { sx_crossing=temptile; crossing=sx_crossing; }
cTrack.DropRailHere((0-(rail+1)), temptile, stationObj.s_ID, useEntry); // remove the test track
}
j++;
} while (j < 5 && !success);
if (success)
{
cStation.StationClaimTile(crossing, stationObj.s_ID, useEntry);
if (useEntry)
{
stationObj.s_EntrySide[TrainSide.CROSSING]= se_crossing;
DInfo("Entry crossing is now set to : "+se_crossing,2);
}
else
{
stationObj.s_ExitSide[TrainSide.CROSSING]= sx_crossing;
DInfo("Exit crossing is now set to : "+sx_crossing,2);
}
cError.ClearError();
return true;
}
return false;
}
function cBuilder::RailStationPhaseBuildEntrance(stationObj, useEntry, tmptaker, road)
{
DInfo("--- Phase4: build entry&exit for IN/OUT",1);
local in_str="";
local direction=stationObj.GetRailStationDirection();
local railCross, railFront, railLeft, railRight, railUpLeft, railUpRight, rail = null;
if (direction == AIRail.RAILTRACK_NW_SE)
{
railFront=AIRail.RAILTRACK_NW_SE;
railCross=AIRail.RAILTRACK_NE_SW;
if (useEntry) // going NW->SE
{
railLeft=AIRail.RAILTRACK_SW_SE;
railRight=AIRail.RAILTRACK_NE_SE;
railUpLeft=AIRail.RAILTRACK_NW_SW;
railUpRight=AIRail.RAILTRACK_NW_NE;
}
else // going SE->NW
{
railLeft=AIRail.RAILTRACK_NW_NE;
railRight=AIRail.RAILTRACK_NW_SW;
railUpLeft=AIRail.RAILTRACK_NE_SE;
railUpRight=AIRail.RAILTRACK_SW_SE;
}
}
else // NE_SW
{
railFront=AIRail.RAILTRACK_NE_SW;
railCross=AIRail.RAILTRACK_NW_SE;
if (useEntry) // going NE->SW
{
railLeft=AIRail.RAILTRACK_NW_SW;
railRight=AIRail.RAILTRACK_SW_SE;
railUpLeft=AIRail.RAILTRACK_NW_NE;
railUpRight=AIRail.RAILTRACK_NE_SE;
}
else // going SW->NE
{
railLeft=AIRail.RAILTRACK_NE_SE;
railRight=AIRail.RAILTRACK_NW_NE;
railUpLeft=AIRail.RAILTRACK_SW_SE;
railUpRight=AIRail.RAILTRACK_NW_SW;
}
}
local leftTileOf=cStationRail.GetRelativeTileLeft(stationObj.s_ID, useEntry);
local rightTileOf=cStationRail.GetRelativeTileRight(stationObj.s_ID, useEntry);
local forwardTileOf=cStationRail.GetRelativeTileForward(stationObj.s_ID, useEntry);
local backwardTileOf=cStationRail.GetRelativeTileBackward(stationObj.s_ID, useEntry);
local rail=railFront;
local j=1;
local fromtile=-1;
local sigtype=AIRail.SIGNALTYPE_PBS;
local position = stationObj.GetLocation()
local sigdir=0;
local se_IN=stationObj.GetRailStationIN(true);
local se_OUT=stationObj.GetRailStationOUT(true);
local sx_IN=stationObj.GetRailStationIN(false);
local sx_OUT=stationObj.GetRailStationOUT(false);
if (useEntry)
{
fromtile=stationObj.s_EntrySide[TrainSide.CROSSING];
if (tmptaker) { if (se_IN!=-1) { tmptaker=false; } }
else { if (se_OUT!=-1) { tmptaker=true; } }
if (tmptaker) { in_str="entry IN"; }
else { in_str="entry OUT"; fromtile+=rightTileOf; }
}
else
{
fromtile = stationObj.s_ExitSide[TrainSide.CROSSING];
if (tmptaker) { if (sx_IN!=-1) { tmptaker=false; } }
else { if (sx_OUT!=-1) { tmptaker=true; } }
if (tmptaker) { in_str="exit IN"; }
else { in_str="exit OUT"; fromtile+=rightTileOf; }
}
DInfo("Building "+in_str+" point",1);
cTrack.StationKillRailDepot(stationObj.s_EntrySide[TrainSide.DEPOT], stationObj.s_ID);
cTrack.StationKillRailDepot(stationObj.s_ExitSide[TrainSide.DEPOT], stationObj.s_ID);
local endconnector=fromtile;
local success=false;
local building_maintrack=true;
if (!road) { building_maintrack=true; }
else { if (road.Primary_RailLink) { building_maintrack=false; }}
do {
local temptile=fromtile+(j*forwardTileOf);
for (local kb = 0; kb < 6; kb++) { cTileTools.DemolishTile(j+(kb*forwardTileOf)); }
cTileTools.TerraformLevelTiles(position,temptile);
if (cTileTools.CanUseTile(temptile,stationObj.s_ID))
{
success = cTrack.DropRailHere(rail, temptile, stationObj.s_ID, useEntry);
}
else { cError.RaiseError(); return false; }
if (!success) { return false; }
if (building_maintrack) // we're building IN/OUT point for the primary track
{
cDebug.PutSign(temptile+(1*forwardTileOf),"R1");
cDebug.PutSign(temptile+(2*forwardTileOf),"R2");
cTileTools.TerraformLevelTiles(position,temptile+(3*forwardTileOf));
if (cTileTools.CanUseTile(temptile+(1*forwardTileOf), stationObj.s_ID))
{
success = cTrack.DropRailHere(rail, temptile+(1*forwardTileOf), stationObj.s_ID, useEntry);
}
else { cError.RaiseError(); return false; }
if (cTileTools.CanUseTile(temptile+(2*forwardTileOf), stationObj.s_ID))
{
success = cTrack.DropRailHere(rail, temptile+(2*forwardTileOf), stationObj.s_ID, useEntry);
}
else { cError.RaiseError(); return false; }
}
if (tmptaker) { sigdir=fromtile+((j-1)*forwardTileOf); }
else { sigdir=fromtile+((j+1)*forwardTileOf); }
DInfo("Building "+in_str+" point signal",1);
success=cError.ForceAction(AIRail.BuildSignal, temptile, sigdir, sigtype);
if (success)
{
if (AIRail.IsRailDepotTile(fromtile)) { cTrack.StationKillRailDepot(fromtile, stationObj.s_ID); }
if (tmptaker)
{
if (useEntry)
{
se_IN = temptile;
stationObj.s_EntrySide[TrainSide.IN]= se_IN;
if (building_maintrack) { stationObj.s_EntrySide[TrainSide.IN_LINK]= se_IN+(3*forwardTileOf); } // link
else { stationObj.s_EntrySide[TrainSide.IN_LINK]= se_IN+forwardTileOf; }
if (!cTileTools.DemolishTile(stationObj.s_EntrySide[TrainSide.IN_LINK]))
{ cError.RaiseError(); return false;}
}
else
{
sx_IN = fromtile + (j*forwardTileOf);
stationObj.s_ExitSide[TrainSide.IN]= sx_IN;
if (building_maintrack) { stationObj.s_ExitSide[TrainSide.IN_LINK]= sx_IN+(3*forwardTileOf); } // link
else { stationObj.s_ExitSide[TrainSide.IN_LINK]= sx_IN+forwardTileOf; }
if (!cTileTools.DemolishTile(stationObj.s_ExitSide[TrainSide.IN_LINK]))
{ cError.RaiseError(); return false;}
}
}
else
{
if (cTileTools.CanUseTile(fromtile,stationObj.s_ID))
{ cTrack.DropRailHere(railUpLeft, fromtile, stationObj.s_ID, useEntry); }
if (useEntry)
{
se_OUT = temptile;
stationObj.s_EntrySide[TrainSide.OUT]= se_OUT;
if (building_maintrack) { stationObj.s_EntrySide[TrainSide.OUT_LINK]= se_OUT+(3*forwardTileOf); } // link
else { stationObj.s_EntrySide[TrainSide.OUT_LINK]= se_OUT+(1*forwardTileOf); }
if (!cTileTools.DemolishTile(stationObj.s_EntrySide[TrainSide.OUT_LINK]))
{ cError.RaiseError(); return false;}
cBuilder.RailConnectorSolver(fromtile+forwardTileOf, fromtile, true);
// this build rails at crossing point connecting to 1st rail of se_OUT
cStation.StationClaimTile(fromtile+forwardTileOf, stationObj.s_ID, useEntry);
}
else
{
sx_OUT = temptile;
stationObj.s_ExitSide[TrainSide.OUT]= sx_OUT;
if (building_maintrack) { stationObj.s_ExitSide[TrainSide.OUT_LINK]= sx_OUT+(3*forwardTileOf); } // link
else { stationObj.s_ExitSide[TrainSide.OUT_LINK]= sx_OUT+(1*forwardTileOf); }
if (!cTileTools.DemolishTile(stationObj.s_ExitSide[TrainSide.OUT_LINK]))
{ cError.RaiseError(); return false;}
cBuilder.RailConnectorSolver(fromtile, fromtile+forwardTileOf ,true);
cStation.StationClaimTile(fromtile+forwardTileOf, stationObj.s_ID, useEntry);
}
}
}
j++;
} while (j < 4 && !success);
return success;
}
function cBuilder::RailStationPathfindAltTrack(roadObj)
{
DInfo("--- Phase6: building alternate track",1);
local srcpos, srclink, dstpos, dstlink= null;
local pval = cRoute.RouteRailGetPathfindingLine(roadObj.UID, false);
if (pval == -1) { cError.RaiseError(); return false; }
srclink = pval[0]; srcpos = pval[1]; dstlink=pval[2], dstpos=pval[3];
DInfo("Calling rail pathfinder: srcpos="+srcpos+" srclink="+srclink+" dstpos="+dstpos+" dstlink="+dstlink,2);
local result=cPathfinder.GetStatus([srclink,srcpos],[dstlink,dstpos], roadObj.TargetStation.s_ID, roadObj.Target_RailEntry);
if (result != 2)
{
if (result == -1)
{
DError("We cannot build the alternate track for that station ",1);
if (roadObj.Source_RailEntry) { cStationRail.RailStationCloseEntry(roadObj.SourceStation.s_ID); }
else { cStationRail.RailStationCloseExit(roadObj.SourceStation.s_ID); }
if (roadObj.Target_RailEntry) { cStationRail.RailStationCloseEntry(roadObj.TargetStation.s_ID); }
else { cStationRail.RailStationCloseExit(roadObj.TargetStation.s_ID); }
cPathfinder.CloseTask([srclink,srcpos],[dstlink,dstpos]);
cError.RaiseError();
roadObj.MoreTrain = 3;
return false;
}
else { cError.ClearError(); return false; } // lack money, still pathfinding... just wait to retry later nothing to do
}
else
{
roadObj.Secondary_RailLink=true;
roadObj.MoreTrain = 2;
roadObj.Route_GroupNameSave();
roadObj.SourceStation.SetAlternateLineBuilt();
roadObj.TargetStation.SetAlternateLineBuilt();
cPathfinder.CloseTask([srclink,srcpos],[dstlink,dstpos]);
cBuilder.RailConnectorSolver(dstpos, dstpos+cTileTools.GetForwardRelativeFromDirection(cBuilder.GetDirection(dstlink, dstpos)), true);
}
return true;
}
function cBuilder::RailStationPhaseSignalBuilder(road)
{
local success=true;
DInfo("--- Phase7: building signals",1);
cBanker.RaiseFundsBigTime();
local vehlist=AIList();
vehlist.AddList(AIVehicleList_Station(road.SourceStation.s_ID)); // because station can be in use by more than 1 route
if (!vehlist.IsEmpty())
{
// erf, easy solve, not really nice, but this won't prevent our work on signal (that could stuck a train else)
foreach (vehicle, dummy in vehlist) cCarrier.VehicleSendToDepot(vehicle,DepotAction.SIGNALUPGRADE+road.SourceStation.s_ID);
return false;
}
local srcpos, dstpos = null;
if (road.Source_RailEntry) { srcpos=road.SourceStation.s_EntrySide[TrainSide.IN]; }
else { srcpos=road.SourceStation.s_ExitSide[TrainSide.IN]; }
if (road.Target_RailEntry) { dstpos=road.TargetStation.s_EntrySide[TrainSide.OUT]; }
else { dstpos=road.TargetStation.s_ExitSide[TrainSide.OUT]; }
if (!road.SourceStation.IsRailStationPrimarySignalBuilt())
{
DInfo("Building signals on primary track",2);
if (cBuilder.SignalBuilder(srcpos, dstpos))
{
DInfo("...done",2);
road.SourceStation.RailStationSetPrimarySignalBuilt();
}
else { DInfo("... not all signals were built",2); success=false; }
}
cDebug.ClearSigns();
if (road.Source_RailEntry) { srcpos=road.SourceStation.s_EntrySide[TrainSide.OUT]; }
else { srcpos=road.SourceStation.s_ExitSide[TrainSide.OUT]; }
if (road.Target_RailEntry) { dstpos=road.TargetStation.s_EntrySide[TrainSide.IN]; }
else { dstpos=road.TargetStation.s_ExitSide[TrainSide.IN]; }
if (!road.TargetStation.IsRailStationSecondarySignalBuilt())
{
DInfo("Building signals on secondary track",2);
if (cBuilder.SignalBuilder(dstpos, srcpos))
{
DInfo("...done",2);
road.TargetStation.RailStationSetSecondarySignalBuilt();
}
else { DInfo("... not all signals were built",2); success = false; }
}
foreach (vehicle, dummy in vehlist)
{
if (cCarrier.ToDepotList.HasItem(vehicle)) { cCarrier.ToDepotList.RemoveItem(vehicle); }
cCarrier.TrainExitDepot(vehicle);
}
return success;
}
function cBuilder::RailStationPhaseBuildDepot(stationObj, useEntry)
{
DInfo("--- Phase8: build depot",1);
// build depot for it,
// in order to build cleaner rail we build the depot where the OUT line should goes, reserving space for it
// we may need to build entry & exit depot at the same time, so 2 runs
local se_IN=stationObj.GetRailStationIN(true);
local se_OUT=stationObj.GetRailStationOUT(true);
local sx_IN=stationObj.GetRailStationIN(false);
local sx_OUT=stationObj.GetRailStationOUT(false);
local leftTileOf=cStationRail.GetRelativeTileLeft(stationObj.s_ID, useEntry);
local rightTileOf=cStationRail.GetRelativeTileRight(stationObj.s_ID, useEntry);
local forwardTileOf=cStationRail.GetRelativeTileForward(stationObj.s_ID, useEntry);
local backwardTileOf=cStationRail.GetRelativeTileBackward(stationObj.s_ID, useEntry);
local se_crossing=stationObj.s_EntrySide[TrainSide.CROSSING];
local sx_crossing=stationObj.s_ExitSide[TrainSide.CROSSING];
local entry_build = (se_IN != -1 || se_OUT != -1);
local exit_build = (sx_IN != -1 || sx_OUT != -1);
local tile_OUT=null;
local depot_checker=null;
local removedepot=false;
local crossing = null;
local success=false;
local runTarget=cStationRail.RailStationGetRunnerTarget(stationObj.s_ID);
for (local hh=0; hh < 2; hh++)
{
local stationside=(hh==0); // first run we work on entry, second one on exit
if (stationside && !entry_build) { continue; }
if (!stationside && !exit_build) { continue; }
if (stationside)
{
crossing=se_crossing;
tile_OUT=se_IN;
depot_checker=stationObj.s_EntrySide[TrainSide.DEPOT];
}
else
{
crossing=sx_crossing;
tile_OUT=sx_IN;
depot_checker=stationObj.s_ExitSide[TrainSide.DEPOT];
}
if (!AIRail.IsRailDepotTile(depot_checker)) { depot_checker=-1; }
else { continue; }
if (depot_checker == -1)
{
local topLeftPlatform=stationObj.s_Train[TrainType.PLATFORM_LEFT];
local topRightPlatform=stationObj.s_Train[TrainType.PLATFORM_RIGHT];
local topRL=cStationRail.GetRelativeCrossingPoint(topLeftPlatform, stationside);
local topRR=cStationRail.GetRelativeCrossingPoint(topRightPlatform, stationside);
local depotlocations, depotfront = null;
if (AIGameSettings.GetValue("forbid_90_deg") == 0)
{
depotlocations=[topRL+forwardTileOf, topRR+forwardTileOf, topRL+rightTileOf, topRL+leftTileOf, topRR+rightTileOf, topRR+leftTileOf, topRL+leftTileOf+leftTileOf, topRR+rightTileOf+rightTileOf];
depotfront=[topRL, topRL, topRR, topRR, topRL, topRR, topRL+leftTileOf, topRR+rightTileOf];
}
else
{
depotlocations=[topRL+leftTileOf, topRR+rightTileOf, topRL+leftTileOf+leftTileOf, topRR+rightTileOf+rightTileOf];
depotfront=[topRL, topRR, topRL+leftTileOf, topRR+rightTileOf];
}
DInfo("Building station depot",1);
for (local h=0; h < depotlocations.len(); h++)
{
cTileTools.TerraformLevelTiles(crossing,depotlocations[h]);
cDebug.PutSign(depotlocations[h],"d");
if (cTileTools.CanUseTile(depotlocations[h],stationObj.s_ID))
{
cTileTools.DemolishTile(depotlocations[h]);
removedepot=AIRail.BuildRailDepot(depotlocations[h], depotfront[h]);
}
local depot_Front=AIRail.GetRailDepotFrontTile(depotlocations[h]);
if (AIMap.IsValidTile(depot_Front)) { success=cBuilder.RailConnectorSolver(depotlocations[h],depot_Front,true); }
if (success) { success=cStation.IsDepot(depotlocations[h]); }
if (success)
{
local runTarget=cStationRail.RailStationGetRunnerTarget(stationObj.s_ID);
if (runTarget != -1) { success= cBuilder.RoadRunner(depotlocations[h], runTarget, AIVehicle.VT_RAIL); }
}
if (success)
{
DInfo("We built depot at "+depotlocations[h],1);
cStation.StationClaimTile(depotlocations[h], stationObj.s_ID, stationside);
if (stationside) { stationObj.s_EntrySide[TrainSide.DEPOT]= depotlocations[h]; }
else { stationObj.s_ExitSide[TrainSide.DEPOT]= depotlocations[h]; }
success=true;
break;
}
else { if (removedepot) { cTileTools.DemolishTile(depotlocations[h]); } }
}
}
} // for loop hh=
return success;
}
function cBuilder::RailStationGrow(staID, useEntry, taker)
// make the station grow and build entry/exit...
// staID: stationID
// useEntry: true to add a train to its entry, false to add it at exit
// taker: true to add a taker train, false to add a dropper train
{
local thatstation=cStation.Load(staID);
if (!thatstation) { return false; }
local trainEntryTaker=thatstation.s_Train[TrainType.TET];
local trainExitTaker=thatstation.s_Train[TrainType.TXT];
local trainEntryDropper=thatstation.s_Train[TrainType.TED];
local trainExitDropper=thatstation.s_Train[TrainType.TXD];
local station_depth=thatstation.s_Train[TrainType.DEPTH];
cTrack.SetRailType(thatstation.s_SubType); // not to forget
local success=false;
local canAddTrain = true;
local closeIt=false;
local PlatformNeedUpdate = false;
if (useEntry)
{
if (taker) { trainEntryTaker++; }
else { trainEntryDropper++; }
}
else
{
if (taker) { trainExitTaker++; }
else { trainExitDropper++; }
}
local trainEntryTotal = trainEntryDropper + trainEntryTaker;
local trainExitTotal = trainExitDropper + trainExitTaker;
local allTaker = trainExitTaker + trainEntryTaker;
local allDropper = trainExitDropper + trainEntryDropper;
local needTaker = allTaker;
local needDropper = allDropper;
if (allTaker > 2) { needTaker = (allTaker >> 1) + (allTaker % 2); }
if (allDropper > 2) { needDropper = (allDropper >> 2) + (allDropper % 4); }
local newStationSize = needTaker + needDropper;
if (allDropper+allTaker == 1) { newStationSize = 0; } // don't grow the station until we have a real train using it
DWarn("STATION : "+thatstation.s_Name,1);
DInfo("allTaker="+allTaker+" allDropper="+allDropper+" needTaker="+needTaker+" needDropper="+needDropper+" newsize="+newStationSize,1);
// find route that use the station
local road=null;
if (thatstation.s_Owner.IsEmpty())
{
DWarn("Nobody claim that station yet",1);
}
else
{
local uidowner=thatstation.s_Train[TrainType.OWNER];
road=cRoute.Load(uidowner);
if (!road) { DInfo("The route owner ID "+uidowner+" is invalid",1); }
else { DInfo("Station main owner "+uidowner,1); }
}
if (useEntry) { DInfo("Working on station entry", 1); }
else { DInfo("Working on station exit", 1); }
local cangrow = (thatstation.s_Size != thatstation.s_MaxSize);
if (cangrow) { DInfo("Station can be upgrade",1); }
else { DInfo("Station is at its maximum size",1); }
local cmpsize = thatstation.s_Train[TrainType.GOODPLATFORM];
if (newStationSize > cmpsize && cangrow) { thatstation.RailStationPhaseUpdate(); cmpsize = thatstation.s_Train[TrainType.GOODPLATFORM]; }
DInfo("Station have "+cmpsize+" working platforms",1);
if (newStationSize > cmpsize)
{
local success = false;
if (cangrow)
{
success = cBuilder.RailStationPhaseGrowing(thatstation, newStationSize, useEntry);
if (!success) { cError.RaiseError(); return false; }
thatstation.DefinePlatform();
PlatformNeedUpdate = true;
}
else { canAddTrain=false; }
}
if ((useEntry && thatstation.s_EntrySide[TrainSide.CROSSING] == -1) || (!useEntry && thatstation.s_ExitSide[TrainSide.CROSSING] == -1))
{
if (!cBuilder.RailStationPhaseDefineCrossing(thatstation, useEntry))
{
if (useEntry) { thatstation.RailStationCloseEntry(); }
else { thatstation.RailStationCloseExit(); }
if (!thatstation.IsRailStationEntryOpen() && !thatstation.IsRailStationExitOpen()) { cError.RaiseError(); }
return false;
}
else { PlatformNeedUpdate=true; }
}
local needIN = 0; local needOUT = 0;
local se_IN=thatstation.GetRailStationIN(true);
local se_OUT=thatstation.GetRailStationOUT(true);
local sx_IN=thatstation.GetRailStationIN(false);
local sx_OUT=thatstation.GetRailStationOUT(false);
if (se_IN == -1 && trainEntryTaker > 0) { needIN++; }
if (sx_IN == -1 && trainExitTaker > 0) { needIN++; }
if (se_OUT == -1 && trainEntryDropper > 0) { needOUT++; }
if (sx_OUT == -1 && trainExitDropper > 0) { needOUT++; }
if ((se_IN == -1 || se_OUT == -1) && trainEntryTotal > 1) { needIN++; needOUT++ }
if ((sx_IN == -1 || sx_OUT == -1) && trainExitTotal > 1) { needIN++; needOUT++ }
local primary = true;
if (!road) { primary = false; }
if (needIN || needOUT > 0)
{
cBuilder.RailStationPhaseBuildEntrance(thatstation, useEntry, taker, road);
se_IN=thatstation.GetRailStationIN(true);
se_OUT=thatstation.GetRailStationOUT(true);
sx_IN=thatstation.GetRailStationIN(false);
sx_OUT=thatstation.GetRailStationOUT(false);
PlatformNeedUpdate=true;
// Try calling the second station to build its entry/exit part too, so we will both reject a new train, but structure will be create for next query
if (cMisc.ValidInstance(road) && thatstation.s_ID == road.SourceStation.s_ID) { cBuilder.RailStationGrow(road.TargetStation.s_ID, road.Target_RailEntry, false); }
}
DInfo("se_IN="+se_IN+" se_OUT="+se_OUT+" sx_IN="+sx_IN+" sx_OUT="+sx_OUT+" canAddTrain="+canAddTrain,2);
local result=true;
if (cMisc.ValidInstance(road) && road.Secondary_RailLink == false && road.SourceStation.s_ID != thatstation.s_ID && (trainEntryTotal >1 || trainExitTotal > 1))
{
if (road.Target_RailEntry) { result = thatstation.IsRailStationEntryOpen(); }
else { result = thatstation.IsRailStationExitOpen(); }
if (result) // don't test if we knows it's dead already
{
result = cBuilder.RailStationPathfindAltTrack(road);
if (!result) { canAddTrain=false; }
}
else { PlatformNeedUpdate=true; } // give the destination station an update chance
}
if (PlatformNeedUpdate && cangrow) { thatstation.RailStationPhaseUpdate(); }
if (cMisc.ValidInstance(road) && road.GroupID != null && road.Secondary_RailLink && (trainEntryTotal > 2 || trainExitTotal > 2) && (!road.SourceStation.IsRailStationPrimarySignalBuilt() || !road.TargetStation.IsRailStationSecondarySignalBuilt()))
{
// build signals
if (!cBuilder.RailStationPhaseSignalBuilder(road)) { canAddTrain=false; }
}
local r_depot = null;
if (useEntry) { r_depot = AIRail.IsRailDepotTile(thatstation.s_EntrySide[TrainSide.DEPOT]); }
else { r_depot = AIRail.IsRailDepotTile(thatstation.s_ExitSide[TrainSide.DEPOT]); }
if (!r_depot) { cBuilder.RailStationPhaseBuildDepot(thatstation, useEntry); }
if (newStationSize > thatstation.s_Train[TrainType.GOODPLATFORM] || ((trainEntryTotal > 1 || trainExitTotal > 1) && !road.Secondary_RailLink)) { DInfo("station "+thatstation.s_Name+" refuse more trains",2); return false; }
return canAddTrain;
}
DictatorAI/train/trainhandler.nut 0000644 0001750 0000144 00000012304 12200545174 016421 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class cTrain extends cClass
// that class handle train, not the train engine, but the vehicle made with train engine + wagon engines
{
static vehicledatabase = {};
function GetTrainObject(vehicleID)
{
return vehicleID in cTrain.vehicledatabase ? cTrain.vehicledatabase[vehicleID] : null;
}
vehicleID = null; // id of the train (it's vehicleID)
numberLocos = null; // number of locos
numberWagons = null; // number of wagons, and wagon!=loco
length = null; // length of the train
srcStationID = null; // the source stationID that train is using
dstStationID = null; // the destination stationID that train is using
src_useEntry = null; // source station is use by its entry=true, exit=false;
dst_useEntry = null; // destination station is use by its entry=true, exit=false;
stationbit = null; // bit0 source station, bit1=destination station :: set to 1 train load or 0 train unload at station
full = null; // set to true if train cannot have more wagons attach to it
wagonPrice = null; // price to buy a new wagon for that train
lastdepotvisit = null; // record last time that train was in a depot
extraengine = null; // true if we have two engines
constructor()
{
vehicleID = null;
numberLocos = 0;
numberWagons = 0;
length = 0;
srcStationID = null;
dstStationID = null;
src_useEntry = null;
dst_useEntry = null;
stationbit = 0;
full = false;
wagonPrice = 0;
lastdepotvisit = 0;
extraengine = false;
this.ClassName = "cTrain";
}
}
function cTrain::Save()
// Save the train in the database
{
if (AIVehicle.GetVehicleType(this.vehicleID)!=AIVehicle.VT_RAIL) { DError("Only supporting train",2); return; }
if (this.vehicleID in cTrain.vehicledatabase) { return; }
cTrain.vehicledatabase[this.vehicleID] <- this;
DInfo("Adding "+cCarrier.GetVehicleName(this.vehicleID)+" to cTrain database",2);
}
function cTrain::Load(tID)
{
local tobj=cTrain();
local inbase=(tID in cTrain.vehicledatabase);
tobj.vehicleID=tID;
if (AIVehicle.IsValidVehicle(tID))
{
if (inbase) { tobj=cTrain.GetTrainObject(tID); }
else { tobj.Save(); }
}
else { cTrain.DeleteVehicle(tID); }
return tobj;
}
function cTrain::TrainSetStation(vehID, stationID, isSource, useEntry, taker)
// set the station properties of a train
{
local train=cTrain.Load(vehID);
if (isSource)
{
train.src_useEntry=useEntry;
train.srcStationID=stationID;
if (taker) { train.stationbit=cMisc.SetBit(train.stationbit, 0); }
}
else
{
train.dst_useEntry=useEntry;
train.dstStationID=stationID;
if (taker) { train.stationbit=cMisc.SetBit(train.stationbit, 1); }
}
DInfo("Train "+cCarrier.GetVehicleName(vehID)+" assign to station "+cStation.GetStationName(stationID),2);
}
function cTrain::DeleteVehicle(vehID)
// delete a vehicle from the database
{
if (vehID in cTrain.vehicledatabase)
{
local atrain=null;
if (AIVehicle.IsValidVehicle(vehID)) { atrain=cTrain.Load(vehID); } // if invalid cTrain.Load call DeleteVehicle ->infinite loop
else { atrain=cTrain(); atrain.vehicleID=vehID; }
DInfo("Removing train "+cCarrier.GetVehicleName(vehID)+" from database",2);
local taker = cMisc.CheckBit(atrain.stationbit, 0);
if (atrain.srcStationID != null) { cStationRail.StationRemoveTrain(taker, atrain.src_useEntry, atrain.srcStationID); }
taker = cMisc.CheckBit(atrain.stationbit, 1);
if (atrain.dstStationID != null) { cStationRail.StationRemoveTrain(taker, atrain.dst_useEntry, atrain.dstStationID); }
delete cTrain.vehicledatabase[vehID];
}
}
function cTrain::IsFull(vehID)
// return the cTrain.full value
{
local train=cTrain.Load(vehID);
return train.full;
}
function cTrain::SetFull(vehID, fullstate)
// set the isFull value of a train
{
local train=cTrain.Load(vehID);
train.full=fullstate;
}
function cTrain::SetWagonPrice(vehID, wprice)
// Set the price for a wagon
{
local train=cTrain.Load(vehID);
train.wagonPrice=wprice;
}
function cTrain::GetWagonPrice(vehID)
// Return the price to buy a new wagon
{
local train=cTrain.Load(vehID);
return train.wagonPrice;
}
function cTrain::IsEmpty(vehID)
// return true if that vehicle have 0 wagons or 0 locos
{
local train=cTrain.Load(vehID);
if (train.numberLocos==0) { return true; }
if (train.numberWagons==0) { return true; }
return false;
}
function cTrain::CanModifyTrain(vehID)
// return true if we can call that vehicle to modify it, else false
{
local train=cTrain.Load(vehID);
local now=AIDate.GetCurrentDate();
return (now-train.lastdepotvisit>60);
}
function cTrain::SetDepotVisit(vehID)
// set the last time a train was in a depot
{
local train=cTrain.Load(vehID);
train.lastdepotvisit=AIDate.GetCurrentDate();
}
DictatorAI/train/railchemin.nut 0000755 0001750 0000144 00000006334 12203277026 016073 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cRoute::DutyOnRailsRoute(uid)
// this is where we handle rails route, that are too specials for common handling
{
local firstveh = false;
local road = cRoute.Load(uid);
if (!road || road.Status != RouteStatus.WORKING) return;
local maxveh=0;
INSTANCE.main.carrier.highcostTrain=0;
local cargoid=road.CargoID;
local railtype=road.SourceStation.s_SubType;
local depot = cRoute.GetDepot(uid);
local futur_engine= cCarrier.ChooseRailCouple(cargoid, railtype, -1);
if (futur_engine[0] == -1) return;
else futur_engine = futur_engine[1]; // the wagon
local futur_engine_capacity = cEngineLib.GetCapacity(futur_engine, road.CargoID);
if (futur_engine_capacity <= 0) return;
road.SourceStation.UpdateStationInfos();
DInfo("After station update",2);
local vehneed=0;
if (road.VehicleCount == 0) { firstveh=true; }
local vehonroute=INSTANCE.main.carrier.GetWagonsInGroup(road.GroupID);
local cargowait=0;
local capacity=0;
local dual=road.SourceProcess.IsTown; // we need to check both side if source is town we're on a dual route (pass or mail)
cargowait=road.SourceStation.s_CargoProduce.GetValue(cargoid);
capacity=road.SourceStation.s_VehicleCapacity.GetValue(cargoid);
if (capacity==0)
{
if (road.SourceProcess.IsTown) cargowait=AITown.GetLastMonthProduction(road.SourceProcess.ID, cargoid);
else cargowait=AIIndustry.GetLastMonthProduction(road.SourceProcess.ID, cargoid);
capacity=futur_engine_capacity;
}
if (dual)
{
road.TargetStation.UpdateStationInfos();
local src_capacity=capacity;
local dst_capacity= road.TargetStation.s_VehicleCapacity.GetValue(cargoid);
local src_wait = cargowait;
local dst_wait = road.TargetStation.s_CargoProduce.GetValue(cargoid);
if (dst_capacity == 0) { dst_wait=AITown.GetLastMonthProduction(road.TargetProcess.ID,cargoid); dst_capacity=futur_engine_capacity; }
if (src_wait < dst_wait) cargowait=src_wait; // keep the lowest cargo amount
else cargowait=dst_wait;
if (src_capacity < dst_capacity) capacity=dst_capacity; // but keep the highest capacity we have
else capacity=src_capacity;
DInfo("Source capacity="+src_capacity+" wait="+src_wait+" --- Target capacity="+dst_capacity+" wait="+dst_wait,2);
}
if (capacity==0) capacity++; // avoid /0
local remain = cargowait - capacity;
if (remain < 0) vehneed=0;
else vehneed = (cargowait / capacity)+1;
if (vehneed > 8) vehneed=8; // limit to a max 8 wagons per trys
DInfo("Route capacity="+capacity+" vehicleneed="+vehneed+" cargowait="+cargowait+" vehicule#="+road.VehicleCount+" firstveh="+firstveh,2);
if (vehneed > 0 && !cCarrier.IsTrainRouteBusy(uid)) INSTANCE.main.carrier.AddWagon(uid,vehneed);
}
DictatorAI/train/railpf.nut 0000755 0001750 0000144 00000042274 12202747600 015237 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
/*
class MyRailPF extends RailPathFinder
{
_cost_level_crossing = null;
}
MyRailPF.Cost._set <- function(idx, val)
{
if (this._main._running) { throw ("You are not allowed to change parameters of a running pathfinder."); }
switch (idx)
{
case "max_cost": this._main._max_cost = val; break;
case "tile": this._main._cost_tile = val; break;
case "diagonal_tile": this._main._cost_diagonal_tile = val; break;
case "turn": this._main._cost_turn = val; break;
case "slope": this._main._cost_slope = val; break;
case "bridge_per_tile": this._main._cost_bridge_per_tile = val; break;
case "tunnel_per_tile": this._main._cost_tunnel_per_tile = val; break;
case "coast": this._main._cost_coast = val; break;
case "max_bridge_length": this._main._max_bridge_length = val; break;
case "max_tunnel_length": this._main._max_tunnel_length = val; break;
default: throw ("the index '" + idx + "' does not exist");
}
return val;
}
MyRailPF.Cost._get <- function(idx)
{
switch (idx)
{
case "max_cost": return this._main._max_cost;
case "tile": return this._main._cost_tile;
case "diagonal_tile": return this._main._cost_diagonal_tile;
case "turn": return this._main._cost_turn;
case "slope": return this._main._cost_slope;
case "bridge_per_tile": return this._main._cost_bridge_per_tile;
case "tunnel_per_tile": return this._main._cost_tunnel_per_tile;
case "coast": return this._main._cost_coast;
case "max_bridge_length": return this._main._max_bridge_length;
case "max_tunnel_length": return this._main._max_tunnel_length;
default: throw ("the index '" + idx + "' does not exist");
}
}
function MyRailPF::_Cost(path, new_tile, new_direction, self)
{
local cost = ::RailPathFinder._Cost(path, new_tile, new_direction, self);
return cost;
}
function MyRailPF::_Estimate(cur_tile, cur_direction, goal_tiles, self)
{
local min_cost = self._max_cost;
/* As estimate we multiply the lowest possible cost for a single tile with
* with the minimum number of tiles we need to traverse. */
/* foreach (tile in goal_tiles)
{
min_cost = cTileTools.GetDistanceChebyshevToTile(cur_tile, tile[0]);
local dx = abs(AIMap.GetTileX(cur_tile) - AIMap.GetTileX(tile[0]));
local dy = abs(AIMap.GetTileY(cur_tile) - AIMap.GetTileY(tile[0]));
min_cost= max(dx, dy)* 3; // the Chebyshev_distance
}
return min_cost;
}
function MyRailPF::FindPath(iterations)
{
//local test_mode = AITestMode();
local ret = this._pathfinder.FindPath(iterations);
this._running = (ret == false) ? true : false;
if (!this._running && ret != null) {
foreach (goal in this._goals) {
print("goals size: "+this._goals.len());
local test_mode = AITestMode();
if (goal[0] == ret.GetTile()) {
return this._pathfinder.Path(ret, goal[1], 0, this._Cost, this);
}
}
}
return ret;
} */
// */
/* $Id: main.nut 15101 2009-01-16 00:05:26Z truebrain $ */
/**
* A Rail Pathfinder.
*/
/*
pf-normal : 58
pf-aystar-6 : 58
*/
class MyRailPF
{
_aystar_class = import("graph.aystar", "", 4);
_max_cost = null; ///< The maximum cost for a route.
_cost_tile = null; ///< The cost for a single tile.
_cost_diagonal_tile = null; ///< The cost for a diagonal tile.
_cost_turn = null; ///< The cost that is added to _cost_tile if the direction changes.
_cost_slope = null; ///< The extra cost if a rail tile is sloped.
_cost_bridge_per_tile = null; ///< The cost per tile of a new bridge, this is added to _cost_tile.
_cost_tunnel_per_tile = null; ///< The cost per tile of a new tunnel, this is added to _cost_tile.
_cost_coast = null; ///< The extra cost for a coast tile.
_pathfinder = null; ///< A reference to the used AyStar object.
_max_bridge_length = null; ///< The maximum length of a bridge that will be build.
_max_tunnel_length = null; ///< The maximum length of a tunnel that will be build.
cost = null; ///< Used to change the costs.
_running = null;
_goals = null;
constructor()
{
this._max_cost = 10000000;
this._cost_tile = 100;
this._cost_diagonal_tile = 70;
this._cost_turn = 50;
this._cost_slope = 100;
this._cost_bridge_per_tile = 150;
this._cost_tunnel_per_tile = 120;
this._cost_coast = 20;
this._max_bridge_length = 6;
this._max_tunnel_length = 6;
this._pathfinder = this._aystar_class(this._Cost, this._Estimate, this._Neighbours, this._CheckDirection, this, this, this, this);
this.cost = this.Cost(this);
this._running = false;
}
/**
* Initialize a path search between sources and goals.
* @param sources The source tiles.
* @param goals The target tiles.
* @param ignored_tiles An array of tiles that cannot occur in the final path.
* @see AyStar::InitializePath()
*/
function InitializePath(sources, goals, ignored_tiles = []) {
local nsources = [];
foreach (node in sources) {
local path = this._pathfinder.Path(null, node[1], 0xFF, this._Cost, this);
path = this._pathfinder.Path(path, node[0], 0xFF, this._Cost, this);
nsources.push(path);
}
this._goals = goals;
this._pathfinder.InitializePath(nsources, goals, ignored_tiles);
}
/**
* Try to find the path as indicated with InitializePath with the lowest cost.
* @param iterations After how many iterations it should abort for a moment.
* This value should either be -1 for infinite, or > 0. Any other value
* aborts immediatly and will never find a path.
* @return A route if one was found, or false if the amount of iterations was
* reached, or null if no path was found.
* You can call this function over and over as long as it returns false,
* which is an indication it is not yet done looking for a route.
* @see AyStar::FindPath()
*/
function FindPath(iterations);
};
class MyRailPF.Cost
{
_main = null;
function _set(idx, val)
{
if (this._main._running) throw("You are not allowed to change parameters of a running pathfinder.");
switch (idx) {
case "max_cost": this._main._max_cost = val; break;
case "tile": this._main._cost_tile = val; break;
case "diagonal_tile": this._main._cost_diagonal_tile = val; break;
case "turn": this._main._cost_turn = val; break;
case "slope": this._main._cost_slope = val; break;
case "bridge_per_tile": this._main._cost_bridge_per_tile = val; break;
case "tunnel_per_tile": this._main._cost_tunnel_per_tile = val; break;
case "coast": this._main._cost_coast = val; break;
case "max_bridge_length": this._main._max_bridge_length = val; break;
case "max_tunnel_length": this._main._max_tunnel_length = val; break;
default: throw("the index '" + idx + "' does not exist");
}
return val;
}
function _get(idx)
{
switch (idx) {
case "max_cost": return this._main._max_cost;
case "tile": return this._main._cost_tile;
case "diagonal_tile": return this._main._cost_diagonal_tile;
case "turn": return this._main._cost_turn;
case "slope": return this._main._cost_slope;
case "bridge_per_tile": return this._main._cost_bridge_per_tile;
case "tunnel_per_tile": return this._main._cost_tunnel_per_tile;
case "coast": return this._main._cost_coast;
case "max_bridge_length": return this._main._max_bridge_length;
case "max_tunnel_length": return this._main._max_tunnel_length;
default: throw("the index '" + idx + "' does not exist");
}
}
constructor(main)
{
this._main = main;
}
};
function MyRailPF::FindPath(iterations)
{
local test_mode = AITestMode();
local ret = this._pathfinder.FindPath(iterations);
this._running = (ret == false) ? true : false;
if (!this._running && ret != null) {
foreach (goal in this._goals) {
if (goal[0] == ret.GetTile()) {
return this._pathfinder.Path(ret, goal[1], 0, this._Cost, this);
}
}
}
return ret;
}
function MyRailPF::_GetBridgeNumSlopes(end_a, end_b)
{
local slopes = 0;
local direction = (end_b - end_a) / AIMap.DistanceManhattan(end_a, end_b);
local slope = AITile.GetSlope(end_a);
if (!((slope == AITile.SLOPE_NE && direction == 1) || (slope == AITile.SLOPE_SE && direction == -AIMap.GetMapSizeX()) ||
(slope == AITile.SLOPE_SW && direction == -1) || (slope == AITile.SLOPE_NW && direction == AIMap.GetMapSizeX()) ||
slope == AITile.SLOPE_N || slope == AITile.SLOPE_E || slope == AITile.SLOPE_S || slope == AITile.SLOPE_W)) {
slopes++;
}
local slope = AITile.GetSlope(end_b);
direction = -direction;
if (!((slope == AITile.SLOPE_NE && direction == 1) || (slope == AITile.SLOPE_SE && direction == -AIMap.GetMapSizeX()) ||
(slope == AITile.SLOPE_SW && direction == -1) || (slope == AITile.SLOPE_NW && direction == AIMap.GetMapSizeX()) ||
slope == AITile.SLOPE_N || slope == AITile.SLOPE_E || slope == AITile.SLOPE_S || slope == AITile.SLOPE_W)) {
slopes++;
}
return slopes;
}
function MyRailPF::_Cost(path, new_tile, new_direction, self)
{
/* path == null means this is the first node of a path, so the cost is 0. */
if (path == null) return 0;
local prev_tile = path.GetTile();
local cost = path.GetCost();
/* If the two tiles are more then 1 tile apart, the pathfinder wants a bridge or tunnel
* to be build. It isn't an existing bridge / tunnel, as that case is already handled. */
if (AIMap.DistanceManhattan(new_tile, prev_tile) > 1)
{
/* Check if we should build a bridge or a tunnel. */
if (AITunnel.GetOtherTunnelEnd(new_tile) == prev_tile)
{
cost += AIMap.DistanceManhattan(new_tile, prev_tile) * (self._cost_tunnel_per_tile);
}
else {
cost += AIMap.DistanceManhattan(new_tile, prev_tile) * (self._cost_bridge_per_tile);
}
}
/* Check for a turn. We do this by substracting the TileID of the current
* node from the TileID of the previous node and comparing that to the
* difference between the tile before the previous node and the node before
* that. */
if (path.GetParent() != null)
{
if (AIMap.DistanceManhattan(path.GetParent().GetTile(), prev_tile) == 1 && path.GetParent().GetTile() - prev_tile != prev_tile - new_tile)
{ cost += self._cost_diagonal_tile; }
if (path.GetParent().GetParent() != null && AIMap.DistanceManhattan(new_tile, path.GetParent().GetParent().GetTile()) == 3 &&
path.GetParent().GetParent().GetTile() - path.GetParent().GetTile() != prev_tile - new_tile) { cost += self._cost_turn; }
}
/* Check if the new tile is a coast tile. */
if (AITile.IsCoastTile(new_tile)) { cost += self._cost_coast; }
/* Check if the last tile was sloped. */
if (AITile.GetSlope(new_tile) != AITile.SLOPE_FLAT) { cost += self._cost_slope; }
return cost;
}
function MyRailPF::_Estimate(cur_tile, cur_direction, goal_tiles, self)
{
local min_cost = self._max_cost;
/* As estimate we multiply the lowest possible cost for a single tile with
* with the minimum number of tiles we need to traverse. */
foreach (tile in goal_tiles) {
local dx = abs(AIMap.GetTileX(cur_tile) - AIMap.GetTileX(tile[0]));
local dy = abs(AIMap.GetTileY(cur_tile) - AIMap.GetTileY(tile[0]));
min_cost = max(dx, dy) * self._cost_tile;
}
return min_cost;
}
function MyRailPF::_Neighbours(path, cur_node, self)
{
if (AITile.HasTransportType(cur_node, AITile.TRANSPORT_RAIL)) return [];
/* self._max_cost is the maximum path cost, if we go over it, the path isn't valid. */
if (path.GetCost() >= self._max_cost) return [];
local tiles = [];
local offsets = [AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1),
AIMap.GetTileIndex(1, 0), AIMap.GetTileIndex(-1, 0)];
if (path.GetParent() != null && AIMap.DistanceManhattan(cur_node, path.GetParent().GetTile()) > 1) {
local other_end = path.GetParent().GetTile();
local next_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
foreach (offset in offsets) {
if (AIRail.BuildRail(cur_node, next_tile, next_tile + offset)) {
tiles.push([next_tile, self._GetDirection(other_end, cur_node, next_tile, true)]);
}
}
} else {
/* Check all tiles adjacent to the current tile. */
foreach (offset in offsets) {
local next_tile = cur_node + offset;
/* Don't turn back */
if (path.GetParent() != null && next_tile == path.GetParent().GetTile()) continue;
/* Disallow 90 degree turns */
if (path.GetParent() != null && path.GetParent().GetParent() != null &&
next_tile - cur_node == path.GetParent().GetParent().GetTile() - path.GetParent().GetTile()) continue;
/* We add them to the to the neighbours-list if we can build a rail to
* them and no rail exists there. */
if ((path.GetParent() == null || AIRail.BuildRail(path.GetParent().GetTile(), cur_node, next_tile))) {
if (path.GetParent() != null) {
tiles.push([next_tile, self._GetDirection(path.GetParent().GetTile(), cur_node, next_tile, false)]);
} else {
tiles.push([next_tile, self._GetDirection(null, cur_node, next_tile, false)]);
}
}
}
if (path.GetParent() != null && path.GetParent().GetParent() != null) {
local bridges = self._GetTunnelsBridges(path.GetParent().GetTile(), cur_node, self._GetDirection(path.GetParent().GetParent().GetTile(), path.GetParent().GetTile(), cur_node, true));
foreach (tile in bridges) {
tiles.push(tile);
}
}
}
return tiles;
}
function MyRailPF::_CheckDirection(tile, existing_direction, new_direction, self)
{
return false;
}
function MyRailPF::_dir(from, to)
{
if (from - to == 1) return 0;
if (from - to == -1) return 1;
if (from - to == AIMap.GetMapSizeX()) return 2;
if (from - to == -AIMap.GetMapSizeX()) return 3;
throw("Shouldn't come here in _dir");
}
function MyRailPF::_GetDirection(pre_from, from, to, is_bridge)
{
if (is_bridge) {
if (from - to == 1) return 1;
if (from - to == -1) return 2;
if (from - to == AIMap.GetMapSizeX()) return 4;
if (from - to == -AIMap.GetMapSizeX()) return 8;
}
return 1 << (4 + (pre_from == null ? 0 : 4 * this._dir(pre_from, from)) + this._dir(from, to));
}
/**
* Get a list of all bridges and tunnels that can be build from the
* current tile. Bridges will only be build starting on non-flat tiles
* for performance reasons. Tunnels will only be build if no terraforming
* is needed on both ends.
*/
function MyRailPF::_GetTunnelsBridges(last_node, cur_node, bridge_dir)
{
local slope = AITile.GetSlope(cur_node);
if (slope == AITile.SLOPE_FLAT && AITile.IsBuildable(cur_node + (cur_node - last_node))) return [];
local tiles = [];
for (local i = 2; i < this._max_bridge_length; i++) {
local bridge_list = AIBridgeList_Length(i + 1);
local target = cur_node + i * (cur_node - last_node);
if (!bridge_list.IsEmpty() && AIBridge.BuildBridge(AIVehicle.VT_RAIL, bridge_list.Begin(), cur_node, target)) {
tiles.push([target, bridge_dir]);
}
}
if (slope != AITile.SLOPE_SW && slope != AITile.SLOPE_NW && slope != AITile.SLOPE_SE && slope != AITile.SLOPE_NE) return tiles;
local other_tunnel_end = AITunnel.GetOtherTunnelEnd(cur_node);
if (!AIMap.IsValidTile(other_tunnel_end)) return tiles;
local tunnel_length = AIMap.DistanceManhattan(cur_node, other_tunnel_end);
local prev_tile = cur_node + (cur_node - other_tunnel_end) / tunnel_length;
if (AITunnel.GetOtherTunnelEnd(other_tunnel_end) == cur_node && tunnel_length >= 2 &&
prev_tile == last_node && tunnel_length < _max_tunnel_length && AITunnel.BuildTunnel(AIVehicle.VT_RAIL, cur_node)) {
tiles.push([other_tunnel_end, bridge_dir]);
}
return tiles;
}
function MyRailPF::_IsSlopedRail(start, middle, end)
{
local NW = 0; // Set to true if we want to build a rail to / from the north-west
local NE = 0; // Set to true if we want to build a rail to / from the north-east
local SW = 0; // Set to true if we want to build a rail to / from the south-west
local SE = 0; // Set to true if we want to build a rail to / from the south-east
if (middle - AIMap.GetMapSizeX() == start || middle - AIMap.GetMapSizeX() == end) NW = 1;
if (middle - 1 == start || middle - 1 == end) NE = 1;
if (middle + AIMap.GetMapSizeX() == start || middle + AIMap.GetMapSizeX() == end) SE = 1;
if (middle + 1 == start || middle + 1 == end) SW = 1;
/* If there is a turn in the current tile, it can't be sloped. */
if ((NW || SE) && (NE || SW)) return false;
local slope = AITile.GetSlope(middle);
/* A rail on a steep slope is always sloped. */
if (AITile.IsSteepSlope(slope)) return true;
/* If only one corner is raised, the rail is sloped. */
if (slope == AITile.SLOPE_N || slope == AITile.SLOPE_W) return true;
if (slope == AITile.SLOPE_S || slope == AITile.SLOPE_E) return true;
if (NW && (slope == AITile.SLOPE_NW || slope == AITile.SLOPE_SE)) return true;
if (NE && (slope == AITile.SLOPE_NE || slope == AITile.SLOPE_SW)) return true;
return false;
}
DictatorAI/train/railpf (copie).nut 0000755 0001750 0000144 00000006623 12202320737 016434 0 ustar krinn users /* -*- Mode: C++; tab-width: 6 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
class MyRailPF extends RailPathFinder
{
_cost_level_crossing = null;
}
MyRailPF.Cost._set <- function(idx, val)
{
if (this._main._running) { throw ("You are not allowed to change parameters of a running pathfinder."); }
switch (idx)
{
case "max_cost": this._main._max_cost = val; break;
case "tile": this._main._cost_tile = val; break;
case "diagonal_tile": this._main._cost_diagonal_tile = val; break;
case "turn": this._main._cost_turn = val; break;
case "slope": this._main._cost_slope = val; break;
case "bridge_per_tile": this._main._cost_bridge_per_tile = val; break;
case "tunnel_per_tile": this._main._cost_tunnel_per_tile = val; break;
case "coast": this._main._cost_coast = val; break;
case "max_bridge_length": this._main._max_bridge_length = val; break;
case "max_tunnel_length": this._main._max_tunnel_length = val; break;
default: throw ("the index '" + idx + "' does not exist");
}
return val;
}
MyRailPF.Cost._get <- function(idx)
{
switch (idx)
{
case "max_cost": return this._main._max_cost;
case "tile": return this._main._cost_tile;
case "diagonal_tile": return this._main._cost_diagonal_tile;
case "turn": return this._main._cost_turn;
case "slope": return this._main._cost_slope;
case "bridge_per_tile": return this._main._cost_bridge_per_tile;
case "tunnel_per_tile": return this._main._cost_tunnel_per_tile;
case "coast": return this._main._cost_coast;
case "max_bridge_length": return this._main._max_bridge_length;
case "max_tunnel_length": return this._main._max_tunnel_length;
default: throw ("the index '" + idx + "' does not exist");
}
}
function MyRailPF::_Cost(path, new_tile, new_direction, self)
{
local cost = ::RailPathFinder._Cost(path, new_tile, new_direction, self);
return cost;
}
function MyRailPF::_Estimate(cur_tile, cur_direction, goal_tiles, self)
{
local min_cost = self._max_cost;
/* As estimate we multiply the lowest possible cost for a single tile with
* with the minimum number of tiles we need to traverse. */
foreach (tile in goal_tiles)
{
min_cost = cTileTools.GetDistanceChebyshevToTile(cur_tile, tile[0]);
/* local dx = abs(AIMap.GetTileX(cur_tile) - AIMap.GetTileX(tile[0]));
local dy = abs(AIMap.GetTileY(cur_tile) - AIMap.GetTileY(tile[0]));
min_cost= max(dx, dy)* 3; // the Chebyshev_distance*/
}
return min_cost;
}
function MyRailPF::FindPath(iterations)
{
//local test_mode = AITestMode();
local ret = this._pathfinder.FindPath(iterations);
this._running = (ret == false) ? true : false;
if (!this._running && ret != null) {
foreach (goal in this._goals) {
print("goals size: "+this._goals.len());
local test_mode = AITestMode();
if (goal[0] == ret.GetTile()) {
return this._pathfinder.Path(ret, goal[1], 0, this._Cost, this);
}
}
}
return ret;
}
DictatorAI/train/railfollower.nut 0000755 0001750 0000144 00000040501 12203755620 016454 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/*
* This file is part of AdmiralAI.
*
* AdmiralAI is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* AdmiralAI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AdmiralAI. If not, see .
*
* Copyright 2008-2010 Thijs Marinussen
*/
/** @file railfollower.nut A custom rail track finder. */
/**
* A Rail pathfinder for existing rails.
* Original file from AdmiralAI modified to met my needs
*/
class RailFollower extends RailPathFinder
{
}
function RailFollower::_Neighbours(path, cur_node, self)
{
/* self._max_cost is the maximum path cost, if we go over it, the path isn't valid. */
if (path.GetCost() >= self._max_cost) return [];
local offsets = [AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1),
AIMap.GetTileIndex(1, 0), AIMap.GetTileIndex(-1, 0)];
local tiles = [];
if (AITile.HasTransportType(cur_node, AITile.TRANSPORT_RAIL)) {
/* Only use track we own. */
if (!AICompany.IsMine(AITile.GetOwner(cur_node))) return [];
if (AITile.IsStationTile(cur_node)) return [];
/* Check if the current tile is part of a bridge or tunnel. */
if (AIBridge.IsBridgeTile(cur_node) || AITunnel.IsTunnelTile(cur_node)) {
if ((AIBridge.IsBridgeTile(cur_node) && AIBridge.GetOtherBridgeEnd(cur_node) == path.GetParent().GetTile()) ||
(AITunnel.IsTunnelTile(cur_node) && AITunnel.GetOtherTunnelEnd(cur_node) == path.GetParent().GetTile())) {
local other_end = path.GetParent().GetTile();
local next_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
tiles.push([next_tile, self._GetDirection(null, cur_node, next_tile, true)]);
} else if (AIBridge.IsBridgeTile(cur_node)) {
local other_end = AIBridge.GetOtherBridgeEnd(cur_node);;
local prev_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
if (prev_tile == path.GetParent().GetTile()) tiles.push([AIBridge.GetOtherBridgeEnd(cur_node), self._GetDirection(null, path.GetParent().GetTile(), cur_node, true)]);
} else {
local other_end = AITunnel.GetOtherTunnelEnd(cur_node);
local prev_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
if (prev_tile == path.GetParent().GetTile()) tiles.push([AITunnel.GetOtherTunnelEnd(cur_node), self._GetDirection(null, path.GetParent().GetTile(), cur_node, true)]);
}
} else {
foreach (offset in offsets) {
local next_tile = cur_node + offset;
/* Don't turn back */
if (next_tile == path.GetParent().GetTile()) continue;
/* Disallow 90 degree turns */
if (path.GetParent().GetParent() != null &&
next_tile - cur_node == path.GetParent().GetParent().GetTile() - path.GetParent().GetTile()) continue;
if (AIRail.AreTilesConnected(path.GetParent().GetTile(), cur_node, next_tile)) {
tiles.push([next_tile, self._GetDirection(path.GetParent().GetTile(), cur_node, next_tile, false)]);
}
}
}
}
return tiles;
}
function RailFollower::FindRouteRails(source, target)
{
if (!AIMap.IsValidTile(source) || !AIMap.IsValidTile(target)) return AIList();
local solve = cBuilder.RoadRunnerHelper(source, target, AIVehicle.VT_RAIL);
return solve;
}
function RailFollower::GetRailPathing(source, target)
// return an AIList with rails from the path, empty AIList on error
{
local pathwalker = RailFollower();
pathwalker.InitializePath([source], [target]);
local path = pathwalker.FindPath(20000);
if (path == null) { DError("Pathwalking failure.",2); return AIList(); }
local toAIList=AIList();
local prev = path.GetTile();
local tile = null;
while (path != null)
{
local tile = path.GetTile();
toAIList.AddItem(tile, 0);
prev = tile;
path = path.GetParent();
}
return toAIList;
}
function RailFollower::FindRailOwner()
// find route owning rails
{
cRoute.RouteDamage.Valuate(AITile.HasTransportType, AITile.TRANSPORT_RAIL);
cRoute.RouteDamage.KeepValue(1);
local rail_routes = AIGroupList();
rail_routes.Valuate(AIGroup.GetVehicleType);
rail_routes.KeepValue(AIVehicle.VT_RAIL);
local uid_list = [];
local rebuildentry = [];
foreach (grp, value in rail_routes) if (cRoute.GroupIndexer.HasItem(grp)) uid_list.push(cRoute.GroupIndexer.GetValue(grp));
foreach (uid in uid_list)
{
cDebug.ClearSigns();
local road = cRoute.Load(uid);
if (!road) continue;
// first re-assign trains state to each station (taker, droppper, using entry/exit)
local train_list = AIVehicleList_Group(road.GroupID);
foreach (plat, _ in road.SourceStation.s_Platforms) cBuilder.PlatformConnectors(plat, road.Source_RailEntry);
foreach (plat, _ in road.TargetStation.s_Platforms) cBuilder.PlatformConnectors(plat, road.Target_RailEntry);
foreach (trains, _ in train_list)
{
road.SourceStation.StationAddTrain(true, road.Source_RailEntry);
road.TargetStation.StationAddTrain(false, road.Target_RailEntry);
}
DInfo("Finding rails for route "+road.Name);
if (!road.Primary_RailLink) { DInfo("FindRailOwner mark "+road.UID+" undoable",1); road.RouteIsNotDoable(); continue; }
local stationID = road.SourceStation.s_ID;
local src_target, dst_target, src_link, dst_link = null;
if (road.Source_RailEntry) src_target = road.SourceStation.s_EntrySide[TrainSide.IN];
else src_target = road.SourceStation.s_ExitSide[TrainSide.IN];
if (road.Target_RailEntry) dst_target = road.TargetStation.s_EntrySide[TrainSide.OUT];
else dst_target = road.TargetStation.s_ExitSide[TrainSide.OUT];
local bad = false;
local src_tiles = AIList();
local dst_tiles = AIList();
local test_tiles = AIList();
// Find main line tracks (source station -> destination station)
src_link = src_target + cStationRail.GetRelativeTileBackward(road.SourceStation.s_ID, road.Source_RailEntry);
dst_link = dst_target + cStationRail.GetRelativeTileBackward(road.TargetStation.s_ID, road.Target_RailEntry)
src_tiles = RailFollower.GetRailPathing([src_target, src_link], [dst_target, dst_link]);
bad = (src_tiles.IsEmpty());
DInfo("Main line rails : "+src_tiles.Count(), 2)
if (!src_tiles.IsEmpty())
{
road.SourceStation.SetPrimaryLineBuilt();
road.TargetStation.SetPrimaryLineBuilt();
}
local notbad = false; // to find if at least 1 platform is working, else the station is bad/unusable
// Find each source station platform tracks
foreach (platnum, _ in road.SourceStation.s_Platforms)
{
test_tiles = RailFollower.FindRouteRails(src_target, platnum);
if (!notbad) notbad = (!test_tiles.IsEmpty());
src_tiles.AddList(test_tiles);
}
if (!notbad && !bad) bad = true;
notbad = false;
// Find each target station platform tracks
foreach (platnum, _ in road.TargetStation.s_Platforms)
{
test_tiles = RailFollower.FindRouteRails(dst_target, platnum);
if (!notbad) notbad = (!test_tiles.IsEmpty());
dst_tiles.AddList(test_tiles);
}
if (!notbad && !bad) bad = true;
local bad_alt = false;
// Find the tracks from source depot -> source station
local depot = null;
if (road.Source_RailEntry) depot = road.SourceStation.s_EntrySide[TrainSide.DEPOT];
else depot = road.SourceStation.s_ExitSide[TrainSide.DEPOT];
test_tiles = RailFollower.FindRouteRails(src_target, depot);
src_tiles.AddList(test_tiles);
// Find the tracks from target depot -> target station
if (road.Target_RailEntry) depot = road.TargetStation.s_EntrySide[TrainSide.DEPOT];
else depot = road.TargetStation.s_ExitSide[TrainSide.DEPOT];
test_tiles = RailFollower.FindRouteRails(dst_target, depot);
dst_tiles.AddList(test_tiles);
// Find alternate line tracks (target station -> source station)
if (road.Source_RailEntry) dst_target = road.SourceStation.s_EntrySide[TrainSide.OUT];
else dst_target = road.SourceStation.s_ExitSide[TrainSide.OUT];
if (road.Target_RailEntry) src_target = road.TargetStation.s_EntrySide[TrainSide.IN];
else src_target = road.TargetStation.s_ExitSide[TrainSide.IN];
src_link = src_target + cStationRail.GetRelativeTileBackward(road.TargetStation.s_ID, road.Target_RailEntry);
dst_link = dst_target + cStationRail.GetRelativeTileBackward(road.SourceStation.s_ID, road.Source_RailEntry)
test_tiles = RailFollower.GetRailPathing([src_target, src_link], [dst_target, dst_link]);
DInfo("Alternate line rails : "+test_tiles.Count(), 2)
if (!bad_alt) bad_alt = (test_tiles.IsEmpty());
if (!bad_alt) { road.SourceStation.SetAlternateLineBuilt(); road.TargetStation.SetAlternateLineBuilt(); }
dst_tiles.AddList(test_tiles);
// Remove station tiles out of founded tiles : we don't want any station tile assign as a non station tiles
src_tiles.Valuate(AITile.IsStationTile);
src_tiles.KeepValue(0);
dst_tiles.Valuate(AITile.IsStationTile);
dst_tiles.KeepValue(0);
// Remove all tiles we found from the "unknown" tiles list
cRoute.RouteDamage.RemoveList(src_tiles);
cRoute.RouteDamage.RemoveList(dst_tiles);
// Now assign tiles to their station OtherTiles list, and claim them
cStation.StationClaimTile(src_tiles, road.SourceStation.s_ID, road.Source_RailEntry);
cStation.StationClaimTile(dst_tiles, road.TargetStation.s_ID, road.Target_RailEntry);
road.SourceStation.s_Train[TrainType.OWNER] = road.UID;
road.TargetStation.s_Train[TrainType.OWNER] = road.UID;
local killit = false;
if (bad) killit = true;
else {
// Change alternate track state if it doesn't match its real state
if (bad_alt)
{
if (road.Secondary_RailLink) { road.Secondary_RailLink = false; road.Route_GroupNameSave(); }
local r1, r2 = null;
if (road.Source_RailEntry) r1 = road.SourceStation.s_EntrySide[TrainSide.OUT];
else r1 = road.SourceStation.s_ExitSide[TrainSide.OUT];
if (road.Target_RailEntry) r2 = road.TargetStation.s_EntrySide[TrainSide.IN];
else r2 = road.TargetStation.s_ExitSide[TrainSide.IN];
road.SourceStation.s_TilesOther.AddItem(r1, 0);
road.TargetStation.s_TilesOther.AddItem(r2, 0);
cRoute.RouteDamage.RemoveItem(r1);
cRoute.RouteDamage.RemoveItem(r2);
if (road.SourceStation.GetRailStationOUT(road.Source_RailEntry) != -1 && road.TargetStation.GetRailStationIN(road.Target_RailEntry) != -1) cBuilder.RailStationPathfindAltTrack(road); // pre-run pathfinding
}
if (!road.Secondary_RailLink && !bad_alt) { road.Secondary_RailLink = true; road.Route_GroupNameSave(); }
}
if (killit) {
DInfo("FindRailOwner mark "+road.UID+" undoable",1);
road.RouteIsNotDoable();
}
}
cRoute.RouteDamage.Valuate(AITile.IsStationTile);
cRoute.RouteDamage.RemoveValue(1);
DInfo("Unknown rails remaining : "+cRoute.RouteDamage.Count());
cTrack.RailCleaner(cRoute.RouteDamage);
cRoute.RouteDamage.Clear();
}
function RailFollower::TryUpgradeLine(vehicle)
{
local wagonproto = cEngineLib.GetWagonFromVehicle(vehicle);
if (wagonproto == -1) { print("bad proto"); return -1; }
local wagon_type = AIVehicle.GetWagonEngineType(vehicle, wagonproto);
local cargo = cEngineLib.GetCargoType(wagon_type);
local loco_engine = AIVehicle.GetEngineType(vehicle);
local upgrade_cost = 0;
local uid = cCarrier.VehicleFindRouteIndex(vehicle);
if (uid == null) { print("cannot find routeid"); return -1; }
local road = cRoute.Load(uid);
if (!road) { print("bad road"); return -1; }
local new_railtype = cEngine.RailTypeIsTop(loco_engine, cargo, false);
if (new_railtype == -1 || new_railtype == road.RailType)
{ print("no new railtype"); return -1; }
else { print("BREAKRAIL "+AIRail.GetName(road.RailType)+" will be replace with "+AIRail.GetName(new_railtype)); }
upgrade_cost = road.SourceStation.s_MoneyUpgrade;
DInfo("Cost to upgrade rails : "+upgrade_cost);
if (!cBanker.CanBuyThat(upgrade_cost)) { return 0; }
local temp = AIList();
local all_owners = AIList();
local all_vehicle = AIList();
local all_rails = AIList();
local savetable = {};
temp.AddList(road.SourceStation.s_Owner);
temp.AddList(road.TargetStation.s_Owner);
foreach (o_uid, _ in temp)
{
local r = cRoute.Load(o_uid);
if (!r) continue;
if (r.Status != RouteStatus.WORKING) continue; // keep only good ones
savetable[r.UID] <- r;
all_owners.AddList(r.SourceStation.s_Owner);
all_owners.AddList(r.TargetStation.s_Owner);
all_rails.AddList(r.SourceStation.s_Tiles);
all_rails.AddList(r.TargetStation.s_Tiles);
all_rails.AddList(r.SourceStation.s_TilesOther);
all_rails.AddList(r.TargetStation.s_TilesOther);
local veh = AIVehicleList_Group(r.GroupID);
foreach (v, _ in veh) { all_vehicle.AddItem(v, r.UID); }
}
if (upgrade_cost == 0)
{
DInfo("Number of affected rails : "+all_rails.Count());
cDebug.showLogic(all_rails);
local raw_basic_cost = AIRail.GetBuildCost(new_railtype, AIRail.BT_TRACK) * all_rails.Count();
local raw_sig_cost = AIRail.GetBuildCost(new_railtype, AIRail.BT_SIGNAL) * (all_rails.Count() / 2);
local raw_station_cost = AIRail.GetBuildCost(new_railtype, AIRail.BT_STATION) * (savetable.len() * 10);
upgrade_cost = raw_basic_cost + raw_sig_cost + raw_station_cost;
cDebug.ClearSigns();
foreach (uid in savetable) { uid.SourceStation.s_MoneyUpgrade = upgrade_cost; uid.TargetStation.s_MoneyUpgrade = upgrade_cost; }
}
if (!cBanker.CanBuyThat(upgrade_cost)) { return 0; }
// Ok, let's call trains...
temp = true;
foreach (veh, _ in all_vehicle)
{
local state = AIVehicle.GetState(veh);
if (state != AIVehicle.VS_IN_DEPOT)
{
temp = false;
local sendit = false;
if (!cCarrier.ToDepotList.HasItem(veh)) sendit = true;
if (!sendit)
{
local why = cCarrier.VehicleSendToDepot_GetReason(cCarrier.ToDepotList.GetValue(veh));
if (why != DepotAction.LINEUPGRADE) sendit = true;
}
if (sendit) cCarrier.VehicleSendToDepot(veh, DepotAction.LINEUPGRADE);
}
}
if (!temp) return 0; // if all stopped or no vehicle, temp will remain true
cBanker.RaiseFundsBigTime();
DInfo("Changing "+road.Name+" railtype #"+road.RailType+" to #"+new_railtype,1);
local safekeeper = all_vehicle.Begin();
local safekeeper_depot = AIVehicle.GetLocation(safekeeper);
local wagon_lost = [];
foreach (veh, uid in all_vehicle)
{
local z = cEngineLib.GetNumberOfWagons(veh);
wagon_lost.push(z);
wagon_lost.push(uid);
if (veh != safekeeper) {
cCarrier.VehicleSell(veh, false);
if (cCarrier.ToDepotList.HasItem(veh)) cCarrier.ToDepotList.RemoveItem(veh);
}
}
all_rails.RemoveItem(safekeeper_depot);
foreach (tiles, _ in all_rails)
{
local err = cTrack.ConvertRailType(tiles, new_railtype);
if (err == -1) {
foreach (o_uid in savetable)
{
DWarn("TryUpgradeLine mark "+o_uid.UID+" undoable",1);
o_uid.RouteIsNotDoable();
}
return -1;
}
if (err == 0) {
DWarn("Cannot convert all rails, redoing later",1);
return 0;
}
}
cCarrier.VehicleSell(safekeeper, false);
if (cCarrier.ToDepotList.HasItem(safekeeper)) cCarrier.ToDepotList.RemoveItem(safekeeper);
if (!cTrack.ConvertRailType(safekeeper_depot, new_railtype)) AITile.DemolishTile(safekeeper_depot);
foreach (uid in savetable)
{
uid.SourceStation.s_MoneyUpgrade = 0;
uid.TargetStation.s_MoneyUpgrade = 0;
uid.SourceStation.s_SubType = new_railtype;
uid.TargetStation.s_SubType = new_railtype;
uid.RailType = new_railtype;
}
INSTANCE.buildDelay = 1;
DInfo("We upgrade route to use railtype #"+new_railtype,0);
do
{
local uid = wagon_lost.pop();
local num = wagon_lost.pop();
print("uid = "+uid+" num="+num);
cCarrier.ForceAddTrain(uid, num);
} while (wagon_lost.len() > 0);
return 1;
}
DictatorAI/water/ 0000755 0001750 0000144 00000000000 12203020236 013211 5 ustar krinn users DictatorAI/water/waterbuilder.nut 0000644 0001750 0000144 00000026706 12203020234 016443 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cBuilder::BuildWaterDepotAtTile(tile, destination)
// Try to build a water depot at tile and nearer
{
local reusedepot = cTileTools.GetTilesAroundPlace(tile, 15);
reusedepot.Valuate(AIMarine.IsWaterDepotTile);
foreach (position, value in reusedepot)
{ // try reusing one, making sure we can reach it
if (value == 0) { continue; }
if (tile == position) { continue; } // avoid runner failure
local reuse = cBuilder.RoadRunner(tile, position, AIVehicle.VT_WATER);
if (reuse) { return position; }
}
reusedepot.KeepValue(0);
reusedepot.Valuate(AITile.GetDistanceManhattanToTile, tile);
reusedepot.RemoveBelowValue(3);
reusedepot.KeepBelowValue(16);
reusedepot.Valuate(AITile.IsWaterTile);
reusedepot.KeepValue(1);
reusedepot.Valuate(AITile.IsStationTile);
reusedepot.KeepValue(0);
reusedepot.Valuate(AIMarine.IsBuoyTile);
reusedepot.KeepValue(0);
reusedepot.Valuate(AIMarine.IsDockTile);
reusedepot.KeepValue(0);
// reusedepot.Valuate(AITile.GetDistanceManhattanToTile, destination);
//reusedepot.Sort(AIList.SORT_BY_VALUE, AIList.SORT_ASCENDING);
local newpos=-1;
foreach (tile, dummy in reusedepot)
{
local dir = cBuilder.GetDirection(tile, destination);
local front = cTileTools.GetForwardRelativeFromDirection(dir);
if (!AITile.IsWaterTile(tile+front) || !(AITile.IsWaterTile(tile+front+front))) { continue; } // boats will stay stuck in it else
newpos = AIMarine.BuildWaterDepot(tile, tile+front);
if (newpos) { return tile; }
}
return -1;
}
function cBuilder::BuildWaterStation(start)
// Build a water station for a route
// @param start true to build at source, false at destination
// @return true or false
{
INSTANCE.main.bank.RaiseFundsBigTime();
local stationtype = null;
local radius = AIStation.GetCoverageRadius(AIStation.STATION_DOCK);
local dir, otherplace =null;
local tilelist = AIList();
if (start)
{
dir = INSTANCE.main.builder.GetDirection(INSTANCE.main.route.SourceProcess.Location, INSTANCE.main.route.TargetProcess.Location);
tilelist = cTileTools.GetTilesAroundPlace(INSTANCE.main.route.SourceProcess.Location, 3 * radius); // 3x for town, 2x industry in cProcess
print("working on "+INSTANCE.main.route.SourceProcess.Name);
if (INSTANCE.main.route.SourceProcess.IsTown)
{
tilelist.Valuate(AITile.IsCoastTile);
tilelist.KeepValue(1);
tilelist.Valuate(AITile.GetCargoAcceptance, INSTANCE.main.route.CargoID, 1, 1, radius);
tilelist.KeepAboveValue(7); // prefer test acceptance to make sure we won't carry over passengers none wants
tilelist.Valuate(AITile.GetCargoProduction, INSTANCE.main.route.CargoID, 1, 1, radius);
tilelist.KeepAboveValue(0); // prefer test acceptance to make sure we won't carry over passengers none wants
}
else {
if (AIIndustry.HasDock(INSTANCE.main.route.SourceProcess.ID))
{
INSTANCE.main.route.SourceStation = AIStation.GetStationID(INSTANCE.main.route.SourceProcess.StationLocation);
local newStation = INSTANCE.main.route.CreateNewStation(true);
if (newStation == null) { return false; }
newStation.s_SubType = -2;
newStation.s_Depot = cBuilder.BuildWaterDepotAtTile(INSTANCE.main.route.SourceProcess.StationLocation, INSTANCE.main.route.TargetProcess.Location);
return true;
}
else {
tilelist.Valuate(AITile.IsCoastTile);
tilelist.KeepValue(1);
tilelist.Valuate(AITile.GetCargoProduction, INSTANCE.main.route.CargoID, 1, 1, radius);
tilelist.KeepAboveValue(0);
}
}
otherplace=INSTANCE.main.route.TargetProcess.Location;
}
else {
dir = INSTANCE.main.builder.GetDirection(INSTANCE.main.route.TargetProcess.Location, INSTANCE.main.route.TargetProcess.Location);
tilelist = cTileTools.GetTilesAroundPlace(INSTANCE.main.route.TargetProcess.Location, 3 * radius); // 3x for town, 2x industry in cProcess
print("working on "+INSTANCE.main.route.TargetProcess.Name);
if (tilelist.IsEmpty()) { print("odd no tiles"); }
if (INSTANCE.main.route.TargetProcess.IsTown)
{
tilelist.Valuate(AITile.IsCoastTile);
tilelist.KeepValue(1);
tilelist.Valuate(AITile.GetCargoAcceptance, INSTANCE.main.route.CargoID, 1, 1, radius);
tilelist.KeepAboveValue(7);
tilelist.Valuate(AITile.GetCargoProduction, INSTANCE.main.route.CargoID, 1, 1, radius);
tilelist.KeepAboveValue(0);
}
else {
if (AIIndustry.HasDock(INSTANCE.main.route.TargetProcess.ID))
{
INSTANCE.main.route.TargetStation = AIStation.GetStationID(INSTANCE.main.route.TargetProcess.StationLocation);
local newStation = INSTANCE.main.route.CreateNewStation(false);
if (newStation == null) { return false; }
newStation.s_SubType = -2;
newStation.s_Depot = cBuilder.BuildWaterDepotAtTile(INSTANCE.main.route.TargetProcess.StationLocation, INSTANCE.main.route.SourceProcess.Location);
return true;
}
else {
tilelist.Valuate(AITile.IsCoastTile);
tilelist.KeepValue(1);
tilelist.Valuate(AITile.GetCargoAcceptance, INSTANCE.main.route.CargoID, 1, 1, radius);
tilelist.KeepAboveValue(7);
}
}
otherplace=INSTANCE.main.route.SourceProcess.Location;
}
local sta_tile = -1;
if (tilelist.IsEmpty()) { sta_tile = -100; print("no tiles") ;}
local testedList = AIList();
testedList.AddList(tilelist);
testedList.Valuate(cBuilder.CanBuildDockAtTile, false); // first check without terraforming
testedList.KeepValue(1);
if (testedList.IsEmpty())
{
print("no tiles without terraforming");
if (!INSTANCE.terraform) { sta_tile = -100; }
else { testedList.AddList(tilelist); sta_tile = -2; }
}
if (sta_tile != -100)
{
testedList.Valuate(AITile.GetDistanceSquareToTile, otherplace);
testedList.Sort(AIList.SORT_BY_VALUE, AIList.SORT_ASCENDING);
foreach (tile, _ in testedList)
{
local success = false;
cDebug.PutSign(tile, ".");
if (sta_tile == -2)
{
success = cBuilder.CanBuildDockAtTile(tile, true);
if (!success) { continue; } // bad tile (no water in front) / lack money / cannot terraform it...
}
success = AIMarine.BuildDock(tile, AIStation.STATION_NEW);
if (success) { sta_tile = tile; break; }
}
if (sta_tile < 0) { sta_tile = -100; }
}
if (sta_tile == -100)
{
DError("Can't find a good place to build the dock !",1);
cError.RaiseError();
return false;
}
if (start) { INSTANCE.main.route.SourceStation = AIStation.GetStationID(sta_tile); }
else { INSTANCE.main.route.TargetStation = AIStation.GetStationID(sta_tile); }
local newstation = INSTANCE.main.route.CreateNewStation(start);
// now the depot
newstation.s_Depot = cBuilder.BuildWaterDepotAtTile(sta_tile, otherplace);
return true;
}
function cBuilder::CanBuildDockAtTile(tile, allow_terraforming)
/**
* Check if we can build a dock at tile, you should pass a coast tile so.
* @param tile The tile to check
* @param allow_terraforming if enable it will terraform the tile to gave a usuable tile
* @return True if the tile is usuable
**/
{
local slope = AITile.GetSlope(tile);
local fronttile = AIList();
fronttile.AddItem(AITile.SLOPE_NW, AIMap.GetTileIndex(0, 1));
fronttile.AddItem(AITile.SLOPE_SW, AIMap.GetTileIndex(-1, 0));
fronttile.AddItem(AITile.SLOPE_NE, AIMap.GetTileIndex(1, 0));
fronttile.AddItem(AITile.SLOPE_SE, AIMap.GetTileIndex(0, -1));
if (fronttile.HasItem(slope) && AITile.IsWaterTile(tile + fronttile.GetValue(slope))) { return true; }
if (!allow_terraforming) { return false; }
local n_slope;
foreach (slopeneed, _ in fronttile)
{
n_slope = slope ^ slopeneed;
local t_slope = slope + n_slope;
if (!fronttile.HasItem(t_slope)) { continue; }
if (!AITile.IsWaterTile(tile +fronttile.GetValue(t_slope))) { continue; }
cDebug.PutSign(tile, "!");
local test = AITestMode();
local result = AITile.RaiseTile(tile, n_slope); // better not waste money
print("simulate result "+result);
test = null;
if (result) { result = AITile.RaiseTile(tile, n_slope); }
return result;
}
return false;
}
function cBuilder::GetDockFrontTile(tile)
/**
* Get the front part of a dock
* @param tile The tile location of a dock
* @param -1 on error, else the dock front tile or the station tile if not a dock
**/
{
local sta_id = AIStation.GetStationID(tile);
if (!AIStation.IsValidStation(sta_id)) return -1;
if (AITile.IsWaterTile(tile)) return tile;
local tiles = cTileTools.GetTilesAroundPlace(tile, 4);
tiles.Valuate(AIStation.GetStationID);
tiles.KeepValue(sta_id);
if (tiles.Count() > 1) { tiles.RemoveItem(tile); }
return tiles.Begin();
}
function cBuilder::RepairWaterRoute(idx)
{
local road = cRoute.Load(idx);
if (!road) { return false; }
cBanker.RaiseFundsBigTime();
if (!AIMarine.IsWaterDepot(road.SourceStation.s_Depot))
{
road.SourceStation.s_Depot = cBuilder.BuildWaterDepotAtTile(road.SourceStation.s_Location, road.TargetStation.s_Location);
}
if (!AIMarine.IsWaterDepot(road.TargetStation.s_Depot))
{
road.TargetStation.s_Depot = cBuilder.BuildWaterDepotAtTile(road.TargetStation.s_Location, road.SourceStation.s_Location);
}
if (!AIMarine.IsWaterDepot(road.SourceStation.s_Depot) && !AIMarine.IsWaterDepot(road.TargetStation.s_Depot))
{
DInfo("RepairWaterRoute mark #"+idx+" undoable",1);
road.RouteIsNotDoable();
}
}
DictatorAI/water/boatbuilder.nut 0000644 0001750 0000144 00000005725 12202255330 016252 0 ustar krinn users /* -*- Mode: C++; tab-width: 4 -*- */
/**
* This file is part of DictatorAI
* (c) krinn@chez.com
*
* It's free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* You should have received a copy of the GNU General Public License
* with it. If not, see .
*
**/
function cCarrier::GetWaterVehicle(routeidx, cargo = -1)
// return the vehicle we will pickup if we build a vehicle on that route
{
local object = cEngineLib.Infos();
object.cargo_id = cargo;
object.engine_type = AIVehicle.VT_WATER;
if (cargo == -1)
{
local road = cRoute.Load(routeidx);
if (!road) return -1;
object.cargo_id = road.CargoID;
object.depot = cRoute.GetDepot(routeidx);
}
local veh = cEngineLib.GetBestEngine(object, cCarrier.VehicleFilterWater);
return veh[0];
}
function cCarrier::CreateWaterVehicle(routeidx)
// Build an aircraft
{
if (!INSTANCE.use_boat) return false;
local road = cRoute.Load(routeidx);
if (!road) return false;
local engineID = cCarrier.GetWaterVehicle(routeidx);
if (engineID == -1) { DWarn("Cannot find any boats to transport that cargo "+cCargo.GetCargoLabel(road.CargoID),1); return false; }
local srcplace = road.SourceStation.s_Location;
local dstplace = road.TargetStation.s_Location;
local homedepot = road.SourceStation.s_Depot;
local altplace=(road.Twoway && road.VehicleCount > 0 && road.VehicleCount % 2 != 0);
if (altplace) homedepot = road.TargetStation.s_Depot;
if (!cStation.IsDepot(homedepot))
{
homedepot = cRoute.GetDepot(routeidx);
if (!cStation.IsDepot(homedepot)) return false;
}
local price = cEngine.GetPrice(engineID);
cBanker.RaiseFundsBy(price);
local vehID = cEngineLib.CreateVehicle(homedepot, engineID, road.CargoID);
if (vehID != -1)
{
DInfo("Just brought a new boat: "+cCarrier.GetVehicleName(vehID),0);
INSTANCE.main.carrier.vehicle_cash -= price;
}
else {
DError("Cannot create the boat "+cEngine.GetName(engineID),2);
return false;
}
local firstorderflag = AIOrder.OF_NONE + AIOrder.OF_FULL_LOAD_ANY;
local secondorderflag = AIOrder.OF_NONE;
if (road.Twoway) { firstorderflag = secondorderflag; }
else { secondorderflag += AIOrder.OF_NO_LOAD; }
AIOrder.AppendOrder(vehID, srcplace, firstorderflag);
AIOrder.AppendOrder(vehID, dstplace, secondorderflag);
AIGroup.MoveVehicle(road.GroupID, vehID);
if (altplace) INSTANCE.main.carrier.VehicleOrderSkipCurrent(vehID);
if (!cCarrier.StartVehicle(vehID)) {
DError("Cannot start the vehicle: "+cCarrier.GetVehicleName(vehID),2);
cCarrier.VehicleSell(vehID, false);
return false;
}
road.VehicleCount++;
return true;
}