Files
2018-07-17 14:15:37 +02:00

559 lines
13 KiB
ActionScript

import regions.regions;
import saving;
import systems;
import resources;
import civilians;
import statuses;
const double ACC_SYSTEM = 2.0;
const double ACC_INTERSYSTEM = 65.0;
const int GOODS_WORTH = 8;
const double CIV_HEALTH = 25.0;
const double CIV_REPAIR = 1.0;
const double BLOCKADE_TIMER = 3.0 * 60.0;
const double DEST_RANGE = 4.0;
tidy class CivilianScript {
uint type = 0;
Object@ origin;
Object@ pathTarget;
Object@ intermediate;
Region@ prevRegion;
Region@ nextRegion;
int moveId = -1;
bool leavingRegion = false, awaitingIntermediate = false;
bool pickedUp = false;
double Health = CIV_HEALTH;
int stepCount = 0;
int income = 0;
bool delta = false;
uint cargoType = CT_Goods;
const ResourceType@ cargoResource;
int cargoWorth = 0;
double get_health() {
return Health;
}
double get_maxHealth(const Civilian& obj) {
return CIV_HEALTH * obj.radius * obj.owner.ModHP.value;
}
void load(Civilian& obj, SaveFile& msg) {
loadObjectStates(obj, msg);
if(msg.readBit()) {
obj.activateMover();
msg >> cast<Savable>(obj.Mover);
}
msg >> type;
msg >> origin;
msg >> pathTarget;
msg >> intermediate;
msg >> prevRegion;
msg >> nextRegion;
msg >> moveId;
msg >> leavingRegion;
msg >> pickedUp;
msg >> Health;
msg >> stepCount;
msg >> income;
msg >> cargoType;
if(msg.readBit())
@cargoResource = getResource(msg.readIdentifier(SI_Resource));
msg >> cargoWorth;
makeMesh(obj);
}
void save(Civilian& obj, SaveFile& msg) {
saveObjectStates(obj, msg);
if(obj.hasMover) {
msg.write1();
msg << cast<Savable>(obj.Mover);
}
else {
msg.write0();
}
msg << type;
msg << origin;
msg << pathTarget;
msg << intermediate;
msg << prevRegion;
msg << nextRegion;
msg << moveId;
msg << leavingRegion;
msg << pickedUp;
msg << Health;
msg << stepCount;
msg << income;
msg << cargoType;
if(cargoResource is null) {
msg.write0();
}
else {
msg.write1();
msg.writeIdentifier(SI_Resource, cargoResource.id);
}
msg << cargoWorth;
}
uint getCargoType() {
if(cargoType == CT_Resource && !pickedUp)
return CT_Goods;
return cargoType;
}
uint getCargoResource() {
if(cargoResource is null)
return uint(-1);
return cargoResource.id;
}
int getCargoWorth() {
return cargoWorth;
}
void setCargoType(Civilian& obj, uint type) {
cargoType = type;
@cargoResource = null;
if(type == CT_Goods)
cargoWorth = GOODS_WORTH * obj.radius * CIV_RADIUS_WORTH;
delta = true;
}
void setCargoResource(Civilian& obj, uint id) {
cargoType = CT_Resource;
@cargoResource = getResource(id);
if(pickedUp)
cargoWorth = cargoResource.cargoWorth * obj.radius * CIV_RADIUS_WORTH;
else
cargoWorth = GOODS_WORTH * obj.radius * CIV_RADIUS_WORTH;
delta = true;
}
void modCargoWorth(int diff) {
cargoWorth += diff;
delta = true;
}
int getStepCount() {
return stepCount;
}
void modStepCount(int mod) {
stepCount += mod;
}
void resetStepCount() {
stepCount = 0;
}
void init(Civilian& obj) {
obj.sightRange = 0;
}
uint getCivilianType() {
return type;
}
void setCivilianType(uint type) {
this.type = type;
}
void modIncome(Civilian& obj, int mod) {
if(obj.owner !is null && obj.owner.valid)
obj.owner.modTotalBudget(+mod, MoT_Trade);
income += mod;
}
void postInit(Civilian& obj) {
if(type == CiT_Freighter && obj.owner !is null)
obj.owner.CivilianTradeShips += 1;
if(type == CiT_Freighter) {
obj.activateMover();
obj.maxAcceleration = ACC_SYSTEM;
obj.rotationSpeed = 1.0;
}
makeMesh(obj);
Health = get_maxHealth(obj);
delta = true;
}
void makeMesh(Civilian& obj) {
MeshDesc mesh;
@mesh.model = getCivilianModel(obj.owner, type, obj.radius);
@mesh.material = getCivilianMaterial(obj.owner, type, obj.radius);
@mesh.iconSheet = getCivilianIcon(obj.owner, type, obj.radius).sheet;
mesh.iconIndex = getCivilianIcon(obj.owner, type, obj.radius).index;
bindMesh(obj, mesh);
}
bool onOwnerChange(Civilian& obj, Empire@ prevOwner) {
if(income != 0 && prevOwner !is null && prevOwner.valid)
prevOwner.modTotalBudget(-income, MoT_Trade);
if(type == CiT_Freighter && prevOwner !is null)
prevOwner.CivilianTradeShips -= 1;
regionOwnerChange(obj, prevOwner);
if(type == CiT_Freighter && obj.owner !is null)
obj.owner.CivilianTradeShips += 1;
if(income != 0 && prevOwner !is null && obj.owner.valid)
obj.owner.modTotalBudget(-income, MoT_Trade);
return false;
}
void destroy(Civilian& obj) {
if((obj.inCombat || obj.engaged) && !game_ending) {
playParticleSystem("ShipExplosion", obj.position, obj.rotation, obj.radius, obj.visibleMask);
}
else {
if(cargoResource !is null) {
for(uint i = 0, cnt = cargoResource.hooks.length; i < cnt; ++i)
cargoResource.hooks[i].onTradeDestroy(obj, origin, pathTarget, null);
}
}
if(origin !is null && origin.hasResources)
origin.setAssignedCivilian(null);
if(pathTarget !is null && pathTarget.isPlanet && pathTarget.owner is obj.owner) {
auto@ status = getStatusType("Blockaded");
if(status !is null)
pathTarget.addStatus(status.id, timer=BLOCKADE_TIMER);
}
leaveRegion(obj);
if(obj.owner !is null && obj.owner.valid) {
if(type == CiT_Freighter)
obj.owner.CivilianTradeShips -= 1;
if(income != 0)
obj.owner.modTotalBudget(-income, MoT_Trade);
}
}
void freeCivilian(Civilian& obj) {
if(origin !is null && origin.hasResources)
origin.setAssignedCivilian(null);
Region@ region = obj.region;
if(region !is null) {
@origin = null;
@pathTarget = null;
@prevRegion = null;
@nextRegion = null;
region.freeUpCivilian(obj);
}
else {
@origin = null;
@pathTarget = null;
@prevRegion = null;
@nextRegion = null;
obj.destroy();
}
}
float timer = 0.f;
void occasional_tick(Civilian& obj) {
//Update in combat flags
bool engaged = obj.engaged;
obj.inCombat = engaged;
obj.engaged = false;
if(engaged && obj.region !is null)
obj.region.EngagedMask |= obj.owner.mask;
}
void gotoTradeStation(Civilian@ station) {
if(!awaitingIntermediate)
return;
awaitingIntermediate = false;
@intermediate = station;
}
void gotoTradePlanet(Planet@ planet) {
if(!awaitingIntermediate)
return;
awaitingIntermediate = false;
@intermediate = planet;
}
double tick(Civilian& obj, double time) {
//Update normal stuff
updateRegion(obj);
if(obj.hasMover)
obj.moverTick(time);
//Tick occasional stuff
timer -= float(time);
if(timer <= 0.f) {
occasional_tick(obj);
timer = 1.f;
}
//Do repair
double maxHP = get_maxHealth(obj);
if(!obj.inCombat && Health < maxHP) {
Health = min(Health + (CIV_REPAIR * time * obj.radius), maxHP);
delta = true;
}
if(awaitingIntermediate)
return 0.25;
//Update pathing
Region@ curRegion = obj.region;
if(pathTarget !is null) {
if(origin !is null && !pickedUp) {
if(obj.moveTo(origin, moveId, distance=10.0, enterOrbit=false)) {
pickedUp = true;
if(cargoResource !is null)
cargoWorth = cargoResource.cargoWorth * obj.radius * CIV_RADIUS_WORTH;
delta = true;
moveId = -1;
return 0.5;
}
else {
return 0.2;
}
}
Region@ destRegion;
if(pathTarget.isRegion)
@destRegion = cast<Region>(pathTarget);
else
@destRegion = pathTarget.region;
if(nextRegion is null) {
if(curRegion is null)
@nextRegion = findNearestRegion(obj.position);
else
@nextRegion = curRegion;
}
if(nextRegion is null || destRegion is null) {
freeCivilian(obj);
return 0.4;
}
if(leavingRegion) {
vec3d enterDest;
if(nextRegion !is destRegion || destRegion is pathTarget || getSystem(prevRegion).isSpatialAdjacent(getSystem(nextRegion))) {
enterDest = nextRegion.position + (prevRegion.position - nextRegion.position).normalized(nextRegion.radius * 0.85);
enterDest += random3d(0.0, DEST_RANGE);
}
else {
enterDest = pathTarget.position + vec3d(0,0,pathTarget.radius+10.0);
}
obj.maxAcceleration = ACC_INTERSYSTEM;
if(!obj.moveTo(enterDest, moveId, enterOrbit=false))
return 0.2;
if(cargoType == CT_Resource)
prevRegion.bumpTradeCounter(obj.owner);
moveId = -1;
leavingRegion = false;
}
if(curRegion is null || (nextRegion !is null && nextRegion is curRegion)) {
if(nextRegion is destRegion) {
//Move to destination
obj.maxAcceleration = ACC_SYSTEM;
if(curRegion is pathTarget || obj.moveTo(pathTarget, moveId, distance=10.0, enterOrbit=false)) {
moveId = -1;
if(cargoType == CT_Resource)
destRegion.bumpTradeCounter(obj.owner);
if(cargoResource !is null && !pathTarget.isRegion) {
for(uint i = 0, cnt = cargoResource.hooks.length; i < cnt; ++i)
cargoResource.hooks[i].onTradeDeliver(obj, origin, pathTarget);
}
freeCivilian(obj);
return 0.4;
}
else {
return 0.2;
}
}
else if(curRegion is null) {
//Move to closest region
vec3d pos = nextRegion.position + (nextRegion.position - obj.position).normalized(nextRegion.radius * 0.85);
obj.maxAcceleration = ACC_INTERSYSTEM;
if(obj.moveTo(pos, moveId, enterOrbit=false)) {
moveId = -1;
return 0.4;
}
else {
return 0.2;
}
}
else {
//Find the next region to path to
TradePath path(obj.owner);
path.generate(getSystem(curRegion), getSystem(destRegion));
if(path.pathSize < 2 || !path.valid) {
freeCivilian(obj);
return 0.4;
}
else {
@prevRegion = curRegion;
@nextRegion = path.pathNode[1].object;
awaitingIntermediate = true;
@intermediate = null;
if(curRegion.hasTradeStation(obj.owner))
curRegion.getTradeStation(obj, obj.owner, obj.position);
else if(cargoType == CT_Goods)
curRegion.getTradePlanet(obj, obj.owner);
else
awaitingIntermediate = false;
leavingRegion = false;
}
}
}
if(!leavingRegion) {
if(intermediate !is null) {
if(obj.moveTo(intermediate, moveId, distance=10.0, enterOrbit=false)) {
moveId = -1;
@intermediate = null;
return 0.4;
}
else {
return 0.2;
}
}
else {
if(moveId == -1 && !getSystem(prevRegion).isSpatialAdjacent(getSystem(nextRegion))) {
leavingRegion = true;
return 0.5;
}
vec3d leaveDest;
if(prevRegion is null)
leaveDest = obj.position;
else
leaveDest = prevRegion.position + (nextRegion.position - prevRegion.position).normalized(prevRegion.radius * 0.85) + random3d(0, DEST_RANGE);
obj.maxAcceleration = ACC_SYSTEM;
if(obj.moveTo(leaveDest, moveId, enterOrbit=false)) {
moveId = -1;
leavingRegion = true;
return 0.5;
}
}
}
}
return 0.2;
}
void setOrigin(Object@ origin) {
@this.origin = origin;
delta = true;
}
void pathTo(Civilian& obj, Object@ origin, Object@ target, Object@ stopAt = null) {
@pathTarget = target;
@prevRegion = null;
@nextRegion = null;
@intermediate = stopAt;
@this.origin = origin;
pickedUp = false;
delta = true;
leavingRegion = false;
}
void pathTo(Civilian& obj, Object@ target) {
@pathTarget = target;
@prevRegion = null;
@nextRegion = null;
@origin = null;
@intermediate = null;
pickedUp = true;
delta = true;
leavingRegion = false;
}
void damage(Civilian& obj, DamageEvent& evt, double position, const vec2d& direction) {
if(!obj.valid || obj.destroying)
return;
obj.engaged = true;
Health = max(0.0, Health - evt.damage);
delta = true;
if(Health <= 0.0) {
if(cargoWorth > 0) {
Empire@ other = evt.obj.owner;
if(other !is null && other.major) {
other.addBonusBudget(cargoWorth);
cargoWorth = 0;
}
}
if(cargoResource !is null) {
for(uint i = 0, cnt = cargoResource.hooks.length; i < cnt; ++i)
cargoResource.hooks[i].onTradeDestroy(obj, origin, pathTarget, evt.obj);
}
obj.destroy();
}
}
void _writeDelta(const Civilian& obj, Message& msg) {
msg.writeSmall(cargoType);
msg.writeSmall(cargoWorth);
msg.writeBit(pickedUp);
msg.writeFixed(obj.health/obj.maxHealth);
if(cargoResource !is null) {
msg.write1();
msg.writeLimited(cargoResource.id, getResourceCount()-1);
}
else {
msg.write0();
}
}
void syncInitial(const Civilian& obj, Message& msg) {
if(obj.hasMover) {
msg.write1();
obj.writeMover(msg);
}
else {
msg.write0();
}
msg << type;
_writeDelta(obj, msg);
}
void syncDetailed(const Civilian& obj, Message& msg) {
if(obj.hasMover) {
msg.write1();
obj.writeMover(msg);
}
else {
msg.write0();
}
_writeDelta(obj, msg);
}
bool syncDelta(const Civilian& obj, Message& msg) {
bool used = false;
if(obj.hasMover && obj.writeMoverDelta(msg))
used = true;
else
msg.write0();
if(delta) {
used = true;
delta = false;
msg.write1();
_writeDelta(obj, msg);
}
else {
msg.write0();
}
return used;
}
};
void dumpPlanetWaitTimes() {
uint cnt = playerEmpire.planetCount;
double avg = 0.0, maxTime = 0.0;
for(uint i = 0; i < cnt; ++i) {
Planet@ pl = playerEmpire.planetList[i];
if(pl !is null && pl.getNativeResourceDestination(playerEmpire, 0) !is null) {
double timer = pl.getCivilianTimer();
print(pl.name+" -- "+timer);
avg += timer;
if(timer > maxTime)
maxTime = timer;
}
}
avg /= double(cnt);
print(" AVERAGE: "+avg);
print(" MAX: "+maxTime);
}