@screeps/engine
Version:
This is a module for Screeps standalone server. See [main repository](https://github.com/screeps/screeps) for more info.
417 lines (356 loc) • 13.5 kB
JavaScript
var utils = require('./../utils'),
rooms = require('./rooms'),
driver = utils.getRuntimeDriver(),
_ = require('lodash'),
C = driver.constants;
var runtimeData, intents, register, globals;
function calcFreePowerLevels() {
var level = Math.floor(Math.pow((runtimeData.user.power || 0) / C.POWER_LEVEL_MULTIPLY, 1 / C.POWER_LEVEL_POW));
var used = Object.keys(runtimeData.userPowerCreeps).length + _.sum(runtimeData.userPowerCreeps, 'level');
return level - used;
}
function data(id) {
return Object.assign({}, runtimeData.userPowerCreeps[id], runtimeData.roomObjects[id]);
}
function _storeGetter(o) {
return new globals.Store(o);
}
exports.make = function(_runtimeData, _intents, _register, _globals) {
runtimeData = _runtimeData;
intents = _intents;
register = _register;
globals = _globals;
if(globals.PowerCreep) {
return;
}
var PowerCreep = register.wrapFn(function(id) {
var _data = data(id);
if(_data.room) {
globals.RoomObject.call(this, _data.x, _data.y, _data.room);
}
this.id = id;
});
PowerCreep.prototype = Object.create(globals.RoomObject.prototype);
PowerCreep.prototype.constructor = PowerCreep;
utils.defineGameObjectProperties(PowerCreep.prototype, data, {
name: (o) => o.name,
my: (o) => o.user == runtimeData.user._id,
owner: (o) => new Object({username: runtimeData.users[o.user].username}),
level: (o) => o.level,
className: (o) => o.className,
hitsMax: (o) => o.hitsMax,
hits: (o) => o.hits,
shard: (o) => o.shard || undefined,
spawnCooldownTime: (o) => o.spawnCooldownTime !== null && o.spawnCooldownTime > Date.now() ? o.spawnCooldownTime : undefined,
deleteTime: (o) => o.deleteTime || undefined,
powers: (o) => _.mapValues(o.powers, i => ({
level: i.level,
cooldown: Math.max(0, (i.cooldownTime || 0) - runtimeData.time)
})),
saying: o => {
if(!o.actionLog || !o.actionLog.say) {
return undefined;
}
if(o.user == runtimeData.user._id) {
return o.actionLog.say.message;
}
return o.actionLog.say.isPublic ? o.actionLog.say.message : undefined;
},
carry: _storeGetter,
store: _storeGetter,
carryCapacity: o => o.storeCapacity,
ticksToLive: (o) => o.ageTime - runtimeData.time,
});
Object.defineProperty(PowerCreep.prototype, 'memory', {
get: function() {
if(this.id && !this.my) {
return undefined;
}
if(_.isUndefined(globals.Memory.powerCreeps) || globals.Memory.powerCreeps === 'undefined') {
globals.Memory.powerCreeps = {};
}
if(!_.isObject(globals.Memory.powerCreeps)) {
return undefined;
}
return globals.Memory.powerCreeps[this.name] = globals.Memory.powerCreeps[this.name] || {};
},
set: function(value) {
if(this.id && !this.my) {
throw new Error('Could not set other player\'s creep memory');
}
if(_.isUndefined(globals.Memory.powerCreeps) || globals.Memory.powerCreeps === 'undefined') {
globals.Memory.powerCreeps = {};
}
if(!_.isObject(globals.Memory.powerCreeps)) {
throw new Error('Could not set creep memory');
}
globals.Memory.powerCreeps[this.name] = value;
}
});
PowerCreep.prototype.toString = register.wrapFn(function() {
return `[powerCreep ${this.name}]`;
});
PowerCreep.prototype.move = register.wrapFn(function(target) {
if(!this.room) {
return C.ERR_BUSY;
}
return globals.Creep.prototype.move.call(this, target);
});
PowerCreep.prototype.moveTo = register.wrapFn(function(firstArg, secondArg, opts) {
if(!this.room) {
return C.ERR_BUSY;
}
return globals.Creep.prototype.moveTo.call(this, firstArg, secondArg, opts);
});
PowerCreep.prototype.moveByPath = register.wrapFn(function(path) {
if(!this.room) {
return C.ERR_BUSY;
}
return globals.Creep.prototype.moveByPath.call(this, path);
});
PowerCreep.prototype.transfer = register.wrapFn(function(target, resourceType, amount) {
if(!this.room) {
return C.ERR_BUSY;
}
return globals.Creep.prototype.transfer.call(this, target, resourceType, amount);
});
PowerCreep.prototype.withdraw = register.wrapFn(function(target, resourceType, amount) {
if(!this.room) {
return C.ERR_BUSY;
}
return globals.Creep.prototype.withdraw.call(this, target, resourceType, amount);
});
PowerCreep.prototype.drop = register.wrapFn(function(resourceType, amount) {
if(!this.room) {
return C.ERR_BUSY;
}
return globals.Creep.prototype.drop.call(this, resourceType, amount);
});
PowerCreep.prototype.pickup = register.wrapFn(function(target) {
if(!this.room) {
return C.ERR_BUSY;
}
return globals.Creep.prototype.pickup.call(this, target);
});
PowerCreep.prototype.say = register.wrapFn(function(message, isPublic) {
if(!this.room) {
return C.ERR_BUSY;
}
return globals.Creep.prototype.say.call(this, message, isPublic);
});
PowerCreep.prototype.spawn = register.wrapFn(function(powerSpawn) {
if(this.room) {
return C.ERR_BUSY;
}
if(!(powerSpawn instanceof globals.StructurePowerSpawn)) {
return C.ERR_INVALID_TARGET;
}
if(!this.my || !powerSpawn.my) {
return C.ERR_NOT_OWNER;
}
if(!utils.checkStructureAgainstController(data(powerSpawn.id), register.objectsByRoom[data(powerSpawn.id).room], data(powerSpawn.room.controller.id))) {
return C.ERR_RCL_NOT_ENOUGH;
}
if(this.spawnCooldownTime) {
return C.ERR_TIRED;
}
if(_.isUndefined(globals.Memory.powerCreeps)) {
globals.Memory.powerCreeps = {};
}
if(_.isObject(globals.Memory.powerCreeps) && _.isUndefined(globals.Memory.powerCreeps[this.name])) {
globals.Memory.powerCreeps[this.name] = {};
}
intents.pushByName('global', 'spawnPowerCreep', {id: powerSpawn.id, name: this.name}, 50);
return C.OK;
});
PowerCreep.prototype.suicide = register.wrapFn(function() {
if(!this.room) {
return C.ERR_BUSY;
}
if(!this.my) {
return C.ERR_NOT_OWNER;
}
intents.pushByName('global', 'suicidePowerCreep', {id: this.id}, 50);
return C.OK;
});
PowerCreep.prototype.delete = register.wrapFn(function(cancel) {
if(this.room) {
return C.ERR_BUSY;
}
if(!this.my) {
return C.ERR_NOT_OWNER;
}
intents.pushByName('global', 'deletePowerCreep', {id: this.id, cancel}, 50);
return C.OK;
});
PowerCreep.prototype.upgrade = register.wrapFn(function(power) {
if(!this.my) {
return C.ERR_NOT_OWNER;
}
if(calcFreePowerLevels() <= 0) {
return C.ERR_NOT_ENOUGH_RESOURCES;
}
if(this.level >= C.POWER_CREEP_MAX_LEVEL) {
return C.ERR_FULL;
}
var powerInfo = C.POWER_INFO[power];
if(!powerInfo || powerInfo.className !== this.className) {
return C.ERR_INVALID_ARGS;
}
var powerData = data(this.id).powers[power];
var powerLevel = powerData ? powerData.level : 0;
if(powerLevel == 5) {
return C.ERR_FULL;
}
if(this.level < powerInfo.level[powerLevel]) {
return C.ERR_FULL;
}
intents.pushByName('global', 'upgradePowerCreep', {id: this.id, power}, 50);
return C.OK;
});
PowerCreep.prototype.usePower = register.wrapFn(function(power, target) {
if(!this.my) {
return C.ERR_NOT_OWNER;
}
if(!this.room) {
return C.ERR_BUSY;
}
if(this.room.controller) {
if (!this.room.controller.isPowerEnabled) {
return C.ERR_INVALID_ARGS;
}
if (!this.room.controller.my && this.room.controller.safeMode) {
return C.ERR_INVALID_ARGS;
}
}
var powerData = data(this.id).powers[power];
var powerInfo = C.POWER_INFO[power];
if(!powerData || !powerData.level || !powerInfo) {
return C.ERR_NO_BODYPART;
}
if(powerData.cooldownTime > runtimeData.time) {
return C.ERR_TIRED;
}
var ops = powerInfo.ops || 0;
if(_.isArray(ops)) {
ops = ops[powerData.level-1];
}
if((data(this.id).store.ops || 0) < ops) {
return C.ERR_NOT_ENOUGH_RESOURCES;
}
if(powerInfo.range) {
if(!target) {
return C.ERR_INVALID_TARGET;
}
if(!this.pos.inRangeTo(target, powerInfo.range)) {
return C.ERR_NOT_IN_RANGE;
}
var currentEffect = _.find(target.effects, i => i.power == power);
if(currentEffect && currentEffect.level > powerData.level && currentEffect.ticksRemaining > 0) {
return C.ERR_FULL;
}
}
intents.set(this.id, 'usePower', {power, id: target ? target.id : undefined});
return C.OK;
});
PowerCreep.prototype.enableRoom = register.wrapFn(function(target) {
if(!this.my) {
return C.ERR_NOT_OWNER;
}
if(!this.room) {
return C.ERR_BUSY;
}
if(!target || !target.id || !register.structures[target.id] || !(target instanceof globals.Structure)) {
register.assertTargetObject(target);
return C.ERR_INVALID_TARGET;
}
if(!target.pos.isNearTo(this.pos)) {
return C.ERR_NOT_IN_RANGE;
}
if(target.structureType != 'controller' || target.safeMode && !target.my) {
return C.ERR_INVALID_TARGET;
}
intents.set(this.id, 'enableRoom', {id: target.id});
return C.OK;
});
PowerCreep.prototype.renew = register.wrapFn(function(target) {
if(!this.my) {
return C.ERR_NOT_OWNER;
}
if(!this.room) {
return C.ERR_BUSY;
}
if(!target || !target.id || !register.structures[target.id] || !(target instanceof globals.StructurePowerBank) && !(target instanceof globals.StructurePowerSpawn)) {
register.assertTargetObject(target);
return C.ERR_INVALID_TARGET;
}
if((target instanceof globals.StructurePowerSpawn) && !utils.checkStructureAgainstController(data(target.id), register.objectsByRoom[data(target.id).room], data(target.room.controller.id))) {
return C.ERR_RCL_NOT_ENOUGH;
}
if(!target.pos.isNearTo(this.pos)) {
return C.ERR_NOT_IN_RANGE;
}
intents.set(this.id, 'renew', {id: target.id});
return C.OK;
});
PowerCreep.prototype.cancelOrder = register.wrapFn(function(name) {
if(!this.room) {
return C.ERR_BUSY;
}
if(!this.my) {
return C.ERR_NOT_OWNER;
}
if(intents.remove(this.id, name)) {
return C.OK;
}
return C.ERR_NOT_FOUND;
});
PowerCreep.prototype.rename = register.wrapFn(function(name) {
if(this.room) {
return C.ERR_BUSY;
}
if(!this.my) {
return C.ERR_NOT_OWNER;
}
if(!name || !_.isString(name) || (name.length > 100)) {
return C.ERR_INVALID_ARGS;
}
if(_.any(runtimeData.userPowerCreeps, {name})) {
return C.ERR_NAME_EXISTS;
}
intents.pushByName('global', 'renamePowerCreep', {id: this.id, name}, 50);
return C.OK;
});
PowerCreep.prototype.notifyWhenAttacked = register.wrapFn(function(enabled) {
if(!this.room) {
return C.ERR_BUSY;
}
if(!this.my) {
return C.ERR_NOT_OWNER;
}
if(!_.isBoolean(enabled)) {
return C.ERR_INVALID_ARGS;
}
if(enabled != data(this.id).notifyWhenAttacked) {
intents.set(this.id, 'notifyWhenAttacked', {enabled});
}
return C.OK;
});
PowerCreep.create = register.wrapFn(function(name, className) {
if(!name || !_.isString(name) || (name.length > 100)) {
return C.ERR_INVALID_ARGS;
}
if(calcFreePowerLevels() <= 0) {
return C.ERR_NOT_ENOUGH_RESOURCES;
}
if(_.any(runtimeData.userPowerCreeps, {name})) {
return C.ERR_NAME_EXISTS;
}
if(Object.values(C.POWER_CLASS).indexOf(className) === -1) {
return C.ERR_INVALID_ARGS;
}
intents.pushByName('global', 'createPowerCreep', {name, className}, 50);
return C.OK;
});
Object.defineProperty(globals, 'PowerCreep', {enumerable: true, value: PowerCreep});
};