Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/components/GuiInterface.js
Show First 20 Lines • Show All 1,162 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
let wallSet = cmd.wallSet; | let wallSet = cmd.wallSet; | ||||
let start = { | let start = { | ||||
"pos": cmd.start, | "pos": cmd.start, | ||||
"angle": 0, | "angle": 0, | ||||
"snapped": false, // did the start position snap to anything? | "snapped": false, // did the start position snap to anything? | ||||
"snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID | "snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID | ||||
"snappedSlot": false | |||||
Freagarach: -`,` | |||||
}; | }; | ||||
let end = { | let end = { | ||||
"pos": cmd.end, | "pos": cmd.end, | ||||
"angle": 0, | "angle": 0, | ||||
"snapped": false, // did the start position snap to anything? | "snapped": false, // did the start position snap to anything? | ||||
"snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID | "snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID | ||||
"snappedSlot": false | |||||
Done Inline Actions-, Freagarach: -`,` | |||||
}; | }; | ||||
// -------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------- | ||||
// do some entity cache management and check for snapping | // do some entity cache management and check for snapping | ||||
if (!this.placementWallEntities) | if (!this.placementWallEntities) | ||||
this.placementWallEntities = {}; | this.placementWallEntities = {}; | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | if (!(tpl in this.placementWallEntities)) | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// prevent division by zero errors further on if the start and end positions are the same | // prevent division by zero errors further on if the start and end positions are the same | ||||
if (end.pos && (start.pos.x === end.pos.x && start.pos.z === end.pos.z)) | if (end.pos && (start.pos.x === end.pos.x && start.pos.z === end.pos.z)) | ||||
end.pos = undefined; | end.pos = undefined; | ||||
// Try to snap to existing slots. | |||||
Done Inline Actions+to +. Freagarach: +`to` +`.` | |||||
if (cmd.snapSlots) | |||||
{ | |||||
// Determined through trial and error. | |||||
Done Inline Actions+. Freagarach: +`.` | |||||
const snapMultiplier = 0.5; | |||||
FreagarachUnsubmitted Done Inline Actionslet? And perhaps a more descriptive name ;) Freagarach: `let`? And perhaps a more descriptive name ;) | |||||
SilierUnsubmitted Done Inline ActionssnapRadiusMultiplier ? Silier: snapRadiusMultiplier ? | |||||
let snapRadius = this.placementWallEntities[wallSet.templates.tower].templateData.wallPiece.length * snapMultiplier; | |||||
let startSnapData = this.GetFoundationSnapData(player, { | |||||
"x": start.pos.x, | |||||
"z": start.pos.z, | |||||
"template": wallSet.templates.tower, | |||||
"snapEntities": cmd.snapSlots, | |||||
"snapRadius": snapRadius | |||||
Done Inline Actions-, Freagarach: -`,` | |||||
}); | |||||
if (startSnapData) | |||||
{ | |||||
start.pos.x = startSnapData.x; | |||||
start.pos.z = startSnapData.z; | |||||
start.angle = startSnapData.angle; | |||||
start.snapped = true; | |||||
start.snappedSlot = true; | |||||
if (startSnapData.ent) | |||||
start.snappedEnt = startSnapData.ent; | |||||
} | |||||
Done Inline ActionsAvoid copies using helper functions. elexis: Avoid copies using helper functions. | |||||
Done Inline ActionsStill this? Freagarach: Still this? | |||||
} | |||||
// See if we need to snap the start and/or end coordinates to any of our list of snap entities. Note that, despite the list | // See if we need to snap the start and/or end coordinates to any of our list of snap entities. Note that, despite the list | ||||
// of snapping candidate entities, it might still snap to e.g. terrain features. Use the "ent" key in the returned snapping | // of snapping candidate entities, it might still snap to e.g. terrain features. Use the "ent" key in the returned snapping | ||||
// data to determine whether it snapped to an entity (if any), and to which one (see GetFoundationSnapData). | // data to determine whether it snapped to an entity (if any), and to which one (see GetFoundationSnapData). | ||||
if (cmd.snapEntities) | if (cmd.snapEntities) | ||||
{ | { | ||||
let snapRadius = this.placementWallEntities[wallSet.templates.tower].templateData.wallPiece.length * 0.5; // determined through trial and error | let snapRadius = this.placementWallEntities[wallSet.templates.tower].templateData.wallPiece.length * 0.5; // determined through trial and error | ||||
let startSnapData = this.GetFoundationSnapData(player, { | let startSnapData = this.GetFoundationSnapData(player, { | ||||
"x": start.pos.x, | "x": start.pos.x, | ||||
Show All 31 Lines | if (end.pos) | ||||
end.angle = endSnapData.angle; | end.angle = endSnapData.angle; | ||||
end.snapped = true; | end.snapped = true; | ||||
if (endSnapData.ent) | if (endSnapData.ent) | ||||
end.snappedEnt = endSnapData.ent; | end.snappedEnt = endSnapData.ent; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Done Inline ActionsNuke Stan: Nuke | |||||
// clear the single-building preview entity (we'll be rolling our own) | // clear the single-building preview entity (we'll be rolling our own) | ||||
this.SetBuildingPlacementPreview(player, { "template": "" }); | this.SetBuildingPlacementPreview(player, { "template": "" }); | ||||
// -------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------- | ||||
// calculate wall placement and position preview entities | // calculate wall placement and position preview entities | ||||
let result = { | let result = { | ||||
"pieces": [], | "pieces": [], | ||||
Show All 19 Lines | GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd) | ||||
// we manually set the control group of the outermost wall pieces equal to those of the snapped-to towers, so | // we manually set the control group of the outermost wall pieces equal to those of the snapped-to towers, so | ||||
// that they are free from mutual obstruction (per definition of obstruction control groups). This is done by | // that they are free from mutual obstruction (per definition of obstruction control groups). This is done by | ||||
// assigning them an extra "controlGroup" field, which we'll then set during the placement loop below. | // assigning them an extra "controlGroup" field, which we'll then set during the placement loop below. | ||||
// Additionally, in the situation that we're snapping to merely a foundation of a tower instead of a fully | // Additionally, in the situation that we're snapping to merely a foundation of a tower instead of a fully | ||||
// constructed one, we'll need an extra preview entity for the starting tower, which also must not be obstructed | // constructed one, we'll need an extra preview entity for the starting tower, which also must not be obstructed | ||||
// by the foundation it snaps to. | // by the foundation it snaps to. | ||||
if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY) | if (start.snappedSlot) | ||||
{ | |||||
let slotEntityObstruction = Engine.QueryInterface(start.snappedEnt, IID_Obstruction); | |||||
FreagarachUnsubmitted Done Inline ActionsCheck? Freagarach: Check? | |||||
previewEntities.unshift({ | |||||
"template": wallSet.templates.tower, | |||||
"pos": start.pos, | |||||
"angle": start.angle, | |||||
"controlGroups": [slotEntityObstruction.GetControlGroup()], | |||||
"slot": true | |||||
}); | |||||
} | |||||
else if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY) | |||||
{ | { | ||||
let startEntObstruction = Engine.QueryInterface(start.snappedEnt, IID_Obstruction); | let startEntObstruction = Engine.QueryInterface(start.snappedEnt, IID_Obstruction); | ||||
if (previewEntities.length > 0 && startEntObstruction) | if (previewEntities.length > 0 && startEntObstruction) | ||||
previewEntities[0].controlGroups = [startEntObstruction.GetControlGroup()]; | previewEntities[0].controlGroups = [startEntObstruction.GetControlGroup()]; | ||||
// if we're snapping to merely a foundation, add an extra preview tower and also set it to the same control group | // if we're snapping to merely a foundation, add an extra preview tower and also set it to the same control group | ||||
let startEntState = this.GetEntityState(player, start.snappedEnt); | let startEntState = this.GetEntityState(player, start.snappedEnt); | ||||
if (startEntState.foundation) | if (startEntState.foundation) | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd) | ||||
// Loop through the preview entities, and construct the subset of them that need to be, and can be, validly constructed | // Loop through the preview entities, and construct the subset of them that need to be, and can be, validly constructed | ||||
// to build at least a part of the wall (meaning that the subset is truncated after the first entity that needs to be, | // to build at least a part of the wall (meaning that the subset is truncated after the first entity that needs to be, | ||||
// but cannot validly be, constructed). See method-level documentation for more details. | // but cannot validly be, constructed). See method-level documentation for more details. | ||||
let allPiecesValid = true; | let allPiecesValid = true; | ||||
let numRequiredPieces = 0; // number of entities that are required to build the entire wall, regardless of validity | let numRequiredPieces = 0; // number of entities that are required to build the entire wall, regardless of validity | ||||
for (let i = 0; i < previewEntities.length; ++i) | // If the first element is a tower slot, only build that preview. | ||||
const length = previewEntities.length && previewEntities[0].slot ? 1 : previewEntities.length; | |||||
Done Inline Actionsmissing an 'h' Stan: missing an 'h' | |||||
FreagarachUnsubmitted Done Inline Actionslet? Freagarach: `let`? | |||||
SilierUnsubmitted Done Inline Actionsconst is fine and better then let because value is not changing in scope Silier: const is fine and better then let because value is not changing in scope | |||||
for (let i = 0; i < length; ++i) | |||||
{ | { | ||||
let entInfo = previewEntities[i]; | let entInfo = previewEntities[i]; | ||||
let ent = null; | let ent = null; | ||||
let tpl = entInfo.template; | let tpl = entInfo.template; | ||||
let tplData = this.placementWallEntities[tpl].templateData; | let tplData = this.placementWallEntities[tpl].templateData; | ||||
let entPool = this.placementWallEntities[tpl]; | let entPool = this.placementWallEntities[tpl]; | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | if (visible) | ||||
// TODO: Handle results of CheckPlacement | // TODO: Handle results of CheckPlacement | ||||
validPlacement = cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement().success; | validPlacement = cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement().success; | ||||
// If a wall piece has two control groups, it's likely a segment that spans | // If a wall piece has two control groups, it's likely a segment that spans | ||||
// between two existing towers. To avoid placing a duplicate wall segment, | // between two existing towers. To avoid placing a duplicate wall segment, | ||||
// check for collisions with entities that share both control groups. | // check for collisions with entities that share both control groups. | ||||
if (validPlacement && entInfo.controlGroups && entInfo.controlGroups.length > 1) | if (validPlacement && entInfo.controlGroups && entInfo.controlGroups.length > 1) | ||||
validPlacement = cmpObstruction.CheckDuplicateFoundation(); | validPlacement = cmpObstruction.CheckDuplicateFoundation(); | ||||
if (entInfo.slot) | |||||
validPlacement = true; | |||||
Done Inline ActionsCould check for this earlier don't check the rest? Freagarach: Could check for this earlier don't check the rest? | |||||
Done Inline ActionsWhere would you put this Stan: Where would you put this | |||||
Done Inline Actionsif (entInfo.slot) validPlacement = true; else { rest of checks; } Like that? Freagarach: ```
if (entInfo.slot)
validPlacement = true;
else
{
rest of checks;
}
```
Like that? | |||||
} | } | ||||
allPiecesValid = allPiecesValid && validPlacement; | allPiecesValid = allPiecesValid && validPlacement; | ||||
// The requirement below that all pieces so far have to have valid positions, rather than only this single one, | // The requirement below that all pieces so far have to have valid positions, rather than only this single one, | ||||
// ensures that no more foundations will be placed after a first invalidly-positioned piece. (It is possible | // ensures that no more foundations will be placed after a first invalidly-positioned piece. (It is possible | ||||
// for pieces past some invalidly-positioned ones to still have valid positions, e.g. if you drag a wall | // for pieces past some invalidly-positioned ones to still have valid positions, e.g. if you drag a wall | ||||
// through and past an existing building). | // through and past an existing building). | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd) | ||||
// If any were entities required to build the wall, but none of them could be validly positioned, return failure | // If any were entities required to build the wall, but none of them could be validly positioned, return failure | ||||
// (see method-level documentation). | // (see method-level documentation). | ||||
if (numRequiredPieces > 0 && result.pieces.length == 0) | if (numRequiredPieces > 0 && result.pieces.length == 0) | ||||
return false; | return false; | ||||
if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY) | if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY) | ||||
result.startSnappedEnt = start.snappedEnt; | result.startSnappedEnt = start.snappedEnt; | ||||
result.rebuildStartTower = start.snappedSlot; | |||||
// We should only return that we snapped to an entity if all pieces up until that entity can be validly constructed, | // We should only return that we snapped to an entity if all pieces up until that entity can be validly constructed, | ||||
// i.e. are included in result.pieces (see docs for the result object). | // i.e. are included in result.pieces (see docs for the result object). | ||||
if (end.pos && end.snappedEnt && end.snappedEnt != INVALID_ENTITY && allPiecesValid) | if (end.pos && end.snappedEnt && end.snappedEnt != INVALID_ENTITY && allPiecesValid) | ||||
result.endSnappedEnt = end.snappedEnt; | result.endSnappedEnt = end.snappedEnt; | ||||
return result; | return result; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 273 Lines • ▼ Show 20 Lines | GuiInterface.prototype.SetMotionDebugOverlay = function(player, data) | ||||
} | } | ||||
}; | }; | ||||
GuiInterface.prototype.SetRangeDebugOverlay = function(player, enabled) | GuiInterface.prototype.SetRangeDebugOverlay = function(player, enabled) | ||||
{ | { | ||||
Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).SetDebugOverlay(enabled); | Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).SetDebugOverlay(enabled); | ||||
}; | }; | ||||
GuiInterface.prototype.GetSlots = function(player, data) | |||||
{ | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | |||||
let entities = cmpRangeManager.GetEntitiesByPlayer(player); | |||||
return entities.filter((ent) => { | |||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); | |||||
return cmpIdentity && cmpIdentity.HasClass("Slot"); | |||||
}); | |||||
}; | |||||
GuiInterface.prototype.SetSlotVisibility = function(player, data) | |||||
{ | |||||
let slots = this.GetSlots(player, undefined); | |||||
Done Inline ActionsEarly return when no slots? Freagarach: Early `return` when no slots? | |||||
if (!slots || !slots.length) | |||||
Done Inline Actionsfilter? Freagarach: `filter`? | |||||
return; | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | |||||
for (let ent of slots) | |||||
{ | |||||
let cmpVisibility = Engine.QueryInterface(ent, IID_Visibility); | |||||
if (!cmpVisibility || cmpVisibility.IsHidden() == data.hidden) | |||||
continue; | |||||
cmpVisibility.SetHidden(data.hidden); | |||||
cmpRangeManager.RequestVisibilityUpdate(ent); | |||||
Done Inline Actionsthis.GetSlots(player, data)? Freagarach: `this.GetSlots(player, data)`? | |||||
} | |||||
}; | |||||
GuiInterface.prototype.GetTraderNumber = function(player) | GuiInterface.prototype.GetTraderNumber = function(player) | ||||
{ | { | ||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | ||||
let traders = cmpRangeManager.GetEntitiesByPlayer(player).filter(e => Engine.QueryInterface(e, IID_Trader)); | let traders = cmpRangeManager.GetEntitiesByPlayer(player).filter(e => Engine.QueryInterface(e, IID_Trader)); | ||||
let landTrader = { "total": 0, "trading": 0, "garrisoned": 0 }; | let landTrader = { "total": 0, "trading": 0, "garrisoned": 0 }; | ||||
let shipTrader = { "total": 0, "trading": 0 }; | let shipTrader = { "total": 0, "trading": 0 }; | ||||
Show All 39 Lines | |||||
}; | }; | ||||
// List the GuiInterface functions that can be safely called by GUI scripts. | // List the GuiInterface functions that can be safely called by GUI scripts. | ||||
// (GUI scripts are non-deterministic and untrusted, so these functions must be | // (GUI scripts are non-deterministic and untrusted, so these functions must be | ||||
// appropriately careful. They are called with a first argument "player", which is | // 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 | // 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.) | // be returned unless this player is meant to be able to see it.) | ||||
let exposedFunctions = { | let exposedFunctions = { | ||||
"GetSimulationState": 1, | "GetSimulationState": 1, | ||||
"GetExtendedSimulationState": 1, | "GetExtendedSimulationState": 1, | ||||
"GetInitAttributes": 1, | "GetInitAttributes": 1, | ||||
"GetReplayMetadata": 1, | "GetReplayMetadata": 1, | ||||
"GetRenamedEntities": 1, | "GetRenamedEntities": 1, | ||||
"ClearRenamedEntities": 1, | "ClearRenamedEntities": 1, | ||||
"GetEntityState": 1, | "GetEntityState": 1, | ||||
Done Inline ActionsIt can't find start here. Freagarach: It can't find `start` here. | |||||
Done Inline ActionsDuh Stan: Duh | |||||
"GetMultipleEntityStates": 1, | "GetMultipleEntityStates": 1, | ||||
"GetAverageRangeForBuildings": 1, | "GetAverageRangeForBuildings": 1, | ||||
"GetTemplateData": 1, | "GetTemplateData": 1, | ||||
"IsTechnologyResearched": 1, | "IsTechnologyResearched": 1, | ||||
"CheckTechnologyRequirements": 1, | "CheckTechnologyRequirements": 1, | ||||
"GetStartedResearch": 1, | "GetStartedResearch": 1, | ||||
"GetBattleState": 1, | "GetBattleState": 1, | ||||
"GetIncomingAttacks": 1, | "GetIncomingAttacks": 1, | ||||
Show All 34 Lines | let exposedFunctions = { | ||||
"SetMotionDebugOverlay": 1, | "SetMotionDebugOverlay": 1, | ||||
"SetRangeDebugOverlay": 1, | "SetRangeDebugOverlay": 1, | ||||
"EnableVisualRangeOverlayType": 1, | "EnableVisualRangeOverlayType": 1, | ||||
"SetRangeOverlays": 1, | "SetRangeOverlays": 1, | ||||
"GetTraderNumber": 1, | "GetTraderNumber": 1, | ||||
"GetTradingGoods": 1, | "GetTradingGoods": 1, | ||||
"IsTemplateModified": 1, | "IsTemplateModified": 1, | ||||
"ResetTemplateModified": 1 | "ResetTemplateModified": 1, | ||||
"GetSlots": 1, | |||||
"SetSlotVisibility": 1 | |||||
Done Inline Actions'GetSlots' Stan: 'GetSlots' | |||||
Done Inline Actions-, Freagarach: -`,` | |||||
}; | }; | ||||
GuiInterface.prototype.ScriptCall = function(player, name, args) | GuiInterface.prototype.ScriptCall = function(player, name, args) | ||||
{ | { | ||||
if (exposedFunctions[name]) | if (exposedFunctions[name]) | ||||
return this[name](player, args); | return this[name](player, args); | ||||
throw new Error("Invalid GuiInterface Call name \""+name+"\""); | throw new Error("Invalid GuiInterface Call name \""+name+"\""); | ||||
}; | }; | ||||
Engine.RegisterSystemComponentType(IID_GuiInterface, "GuiInterface", GuiInterface); | Engine.RegisterSystemComponentType(IID_GuiInterface, "GuiInterface", GuiInterface); |
Wildfire Games · Phabricator
-,