function GuiInterface() { this.notifications = []; } GuiInterface.prototype.Schema = ""; GuiInterface.prototype.Serialize = function() { return {}; }; GuiInterface.prototype.Init = function() { this.placementEntity = undefined; // = undefined or [templateName, entityID] this.rallyPoints = undefined; }; GuiInterface.prototype.PushNotification = function(notification) { this.notifications.push(notification); }; GuiInterface.prototype.PopNotification = function() { //warn(uneval(this.notifications)); if (this.notifications.length) return this.notifications.pop(); else return ""; }; GuiInterface.prototype.GetSimulationState = function(player) { var ret = { "players": [] }; var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); var n = cmpPlayerMan.GetNumPlayers(); for (var i = 0; i < n; ++i) { var playerEnt = cmpPlayerMan.GetPlayerByID(i); var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player); var playerData = { "name": cmpPlayer.GetName(), "civ": cmpPlayer.GetCiv(), "color": cmpPlayer.GetColour(), "popCount": cmpPlayer.GetPopulationCount(), "popLimit": cmpPlayer.GetPopulationLimit(), "resourceCounts": cmpPlayer.GetResourceCounts() }; ret.players.push(playerData); } return ret; }; GuiInterface.prototype.GetEntityState = function(player, ent) { var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); // All units must have a template; if not then it's a nonexistent entity id var template = cmpTempMan.GetCurrentTemplateName(ent); if (!template) return null; var ret = { "id": ent, "template": template } var cmpIdentity = Engine.QueryInterface(ent, IID_Identity); if (cmpIdentity) { ret.identity = { "classes": cmpIdentity.GetClassesList(), }; } var cmpPosition = Engine.QueryInterface(ent, IID_Position); if (cmpPosition) { ret.position = cmpPosition.GetPosition(); } var cmpHealth = Engine.QueryInterface(ent, IID_Health); if (cmpHealth) { ret.hitpoints = cmpHealth.GetHitpoints(); ret.maxHitpoints = cmpHealth.GetMaxHitpoints(); } var cmpAttack = Engine.QueryInterface(ent, IID_Attack); if (cmpAttack) { var type = cmpAttack.GetBestAttack(); // TODO: how should we decide which attack to show? ret.attack = cmpAttack.GetAttackStrengths(type); } var cmpArmour = Engine.QueryInterface(ent, IID_DamageReceiver); if (cmpArmour) { ret.armour = cmpArmour.GetArmourStrengths(); } var cmpBuilder = Engine.QueryInterface(ent, IID_Builder); if (cmpBuilder) { ret.buildEntities = cmpBuilder.GetEntitiesList(); } var cmpTrainingQueue = Engine.QueryInterface(ent, IID_TrainingQueue); if (cmpTrainingQueue) { ret.training = { "entities": cmpTrainingQueue.GetEntitiesList(), "queue": cmpTrainingQueue.GetQueue(), }; } var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation); if (cmpFoundation) { ret.foundation = { "progress": cmpFoundation.GetBuildPercentage() }; } var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership); if (cmpOwnership) { ret.player = cmpOwnership.GetOwner(); } var cmpResourceSupply = Engine.QueryInterface(ent, IID_ResourceSupply); if (cmpResourceSupply) { ret.resourceSupply = { "max": cmpResourceSupply.GetMaxAmount(), "amount": cmpResourceSupply.GetCurrentAmount(), "type": cmpResourceSupply.GetType() }; } var cmpResourceGatherer = Engine.QueryInterface(ent, IID_ResourceGatherer); if (cmpResourceGatherer) { ret.resourceGatherRates = cmpResourceGatherer.GetGatherRates(); } var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint); if (cmpRallyPoint) { ret.rallyPoint = { }; } return ret; }; GuiInterface.prototype.GetTemplateData = function(player, name) { var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); var template = cmpTempMan.GetTemplate(name); if (!template) return null; var ret = {}; if (template.Identity) { ret.name = { "specific": (template.Identity.SpecificName || template.Identity.GenericName), "generic": template.Identity.GenericName }; ret.icon_sheet = template.Identity.IconSheet; ret.icon_cell = template.Identity.IconCell; } if (template.Cost) { ret.cost = {}; if (template.Cost.Resources.food) ret.cost.food = +template.Cost.Resources.food; if (template.Cost.Resources.wood) ret.cost.wood = +template.Cost.Resources.wood; if (template.Cost.Resources.stone) ret.cost.stone = +template.Cost.Resources.stone; if (template.Cost.Resources.metal) ret.cost.metal = +template.Cost.Resources.metal; } return ret; }; GuiInterface.prototype.SetSelectionHighlight = function(player, cmd) { var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); var playerColours = {}; // cache of owner -> colour map for each (var ent in cmd.entities) { var cmpSelectable = Engine.QueryInterface(ent, IID_Selectable); if (!cmpSelectable) continue; if (cmd.alpha == 0) { cmpSelectable.SetSelectionHighlight({"r":0, "g":0, "b":0, "a":0}); continue; } // Find the entity's owner's colour: var owner = -1; var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership); if (cmpOwnership) owner = cmpOwnership.GetOwner(); var colour = playerColours[owner]; if (!colour) { colour = [1, 1, 1]; var cmpPlayer = Engine.QueryInterface(cmpPlayerMan.GetPlayerByID(owner), IID_Player); if (cmpPlayer) colour = cmpPlayer.GetColour(); playerColours[owner] = colour; } cmpSelectable.SetSelectionHighlight({"r":colour.r, "g":colour.g, "b":colour.b, "a":cmd.alpha}); } }; /** * Displays the rally point of a building */ GuiInterface.prototype.DisplayRallyPoint = function(player, cmd) { // If there are rally points already displayed, destroy them for each (var ent in this.rallyPoints) { // Hide it first (the destruction won't be instantaneous) var cmpPosition = Engine.QueryInterface(ent, IID_Position); cmpPosition.MoveOutOfWorld(); Engine.DestroyEntity(ent); } this.rallyPoints = []; var positions = []; // DisplayRallyPoints is called passing a list of entities for which // rally points must be displayed for each (var ent in cmd.entities) { var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint); if (!cmpRallyPoint) continue; // Verify the owner var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership); if (!cmpOwnership || cmpOwnership.GetOwner() != player) continue; // If the command was passed an explicit position, use that and // override the real rally point position; otherwise use the real position var pos; if (cmd.x && cmd.z) pos = {"x": cmd.x, "z": cmd.z}; else pos = cmpRallyPoint.GetPosition(); if (pos) { // TODO: it'd probably be nice if we could draw some kind of line // between the building and pos, to make the marker easy to find even // if it's a long way from the building positions.push(pos); } } // Add rally point entity for each building for each (var pos in positions) { var rallyPoint = Engine.AddLocalEntity("actor|props/special/common/waypoint_flag.xml"); var cmpPosition = Engine.QueryInterface(rallyPoint, IID_Position); cmpPosition.JumpTo(pos.x, pos.z); this.rallyPoints.push(rallyPoint); } }; /** * Display the building placement preview. * cmd.template is the name of the entity template, or "" to disable the preview. * cmd.x, cmd.z, cmd.angle give the location. * Returns true if the placement is okay (everything is valid and the entity is not obstructed by others). */ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd) { // See if we're changing template if (!this.placementEntity || this.placementEntity[0] != cmd.template) { // Destroy the old preview if there was one if (this.placementEntity) Engine.DestroyEntity(this.placementEntity[1]); // Load the new template if (cmd.template == "") { this.placementEntity = undefined; } else { this.placementEntity = [cmd.template, Engine.AddLocalEntity("preview|" + cmd.template)]; } } if (this.placementEntity) { // Move the preview into the right location var pos = Engine.QueryInterface(this.placementEntity[1], IID_Position); if (pos) { pos.JumpTo(cmd.x, cmd.z); pos.SetYRotation(cmd.angle); } // Check whether it's obstructed by other entities var cmpObstruction = Engine.QueryInterface(this.placementEntity[1], IID_Obstruction); var colliding = (cmpObstruction && cmpObstruction.CheckCollisions()); // Set it to a red shade if this is an obstructed location var cmpVisual = Engine.QueryInterface(this.placementEntity[1], IID_Visual); if (cmpVisual) { if (colliding) cmpVisual.SetShadingColour(1.4, 0.4, 0.4, 1); else cmpVisual.SetShadingColour(1, 1, 1, 1); } if (!colliding) return true; } return false; }; GuiInterface.prototype.PlaySound = function(player, data) { PlaySound(data.name, data.entity); }; GuiInterface.prototype.SetPathfinderDebugOverlay = function(player, enabled) { var cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder); cmpPathfinder.SetDebugOverlay(enabled); }; GuiInterface.prototype.SetObstructionDebugOverlay = function(player, enabled) { var cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); cmpObstructionManager.SetDebugOverlay(enabled); }; GuiInterface.prototype.SetMotionDebugOverlay = function(player, data) { for each (var ent in data.entities) { var cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion); if (cmpUnitMotion) cmpUnitMotion.SetDebugOverlay(data.enabled); } }; GuiInterface.prototype.SetRangeDebugOverlay = function(player, enabled) { var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); cmpRangeManager.SetDebugOverlay(enabled); }; // List the GuiInterface functions that can be safely called by GUI scripts. // (GUI scripts are non-deterministic and untrusted, so these functions must be // appropriately careful. They are called with a first argument "player", which is // trusted and indicates the player associated with the current client; no data should // be returned unless this player is meant to be able to see it.) var exposedFunctions = { "PopNotification": 1, "GetSimulationState": 1, "GetEntityState": 1, "GetTemplateData": 1, "SetSelectionHighlight": 1, "DisplayRallyPoint": 1, "SetBuildingPlacementPreview": 1, "PlaySound": 1, "SetPathfinderDebugOverlay": 1, "SetObstructionDebugOverlay": 1, "SetMotionDebugOverlay": 1, "SetRangeDebugOverlay": 1 }; GuiInterface.prototype.ScriptCall = function(player, name, args) { if (exposedFunctions[name]) return this[name](player, args); else throw new Error("Invalid GuiInterface Call name \""+name+"\""); }; Engine.RegisterComponentType(IID_GuiInterface, "GuiInterface", GuiInterface);