0.26.12 Release #34

Merged
Stan merged 5 commits from main into signed 2024-09-10 23:27:32 +02:00
6 changed files with 89 additions and 77 deletions

View File

@ -106,3 +106,4 @@ jobs:
${{ env.MOD_NAME }}-${{ env.MOD_VERSION }}.zip.minisign
target_commitish: ${{ env.MOD_VERSION }}
tag_name: ${{ env.MOD_VERSION }}
name: Community Mod ${{ env.MOD_VERSION }}

View File

@ -1,5 +1,11 @@
[font="sans-bold-24"] Community-mod changelog
[font="sans-bold-20"]
#Version 12 (September 10, 2024)
[font="sans-16"]
- Double the Experience required for Athenian Hoplites to convert into Champion Spearmen.
- Remove walls destroying trees upon completion due to crashes.
[font="sans-bold-20"]
#Version 11 (September 9, 2024)
[font="sans-16"]

View File

@ -1,6 +1,6 @@
{
"name": "community-mod",
"version": "0.26.11",
"version": "0.26.12",
"label": "0 A.D. Community Mod",
"url": "https://gitlab.com/0ad/0ad-community-mod-a26",
"description": "The Community Mod is a community-led effort to improve the gameplay of 0 A.D., officially backed by the 0 A.D. team.",

View File

@ -81,7 +81,8 @@ BuildRestrictions.prototype.Init = function()
* (template name should be "preview|"+templateName), as otherwise territory
* checks for buildings with territory influence will not work as expected.
*/
BuildRestrictions.prototype.CheckPlacement = function () {
BuildRestrictions.prototype.CheckPlacement = function()
{
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
var name = cmpIdentity ? cmpIdentity.GetGenericName() : "Building";
@ -100,7 +101,8 @@ BuildRestrictions.prototype.CheckPlacement = function () {
return result; // Fail
// TODO: AI has no visibility info
if (!cmpPlayer.IsAI()) {
if (!cmpPlayer.IsAI())
{
// Check whether it's in a visible or fogged region
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
@ -108,7 +110,8 @@ BuildRestrictions.prototype.CheckPlacement = function () {
return result; // Fail
var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner()) != "hidden");
if (!explored) {
if (!explored)
{
result.message = markForTranslation("%(name)s cannot be built in unexplored area");
return result; // Fail
}
@ -116,20 +119,21 @@ BuildRestrictions.prototype.CheckPlacement = function () {
// Check obstructions and terrain passability
var passClassName = "";
switch (this.template.PlacementType) {
case "shore":
passClassName = "building-shore";
break;
switch (this.template.PlacementType)
{
case "shore":
passClassName = "building-shore";
break;
case "land-shore":
// 'default-terrain-only' is everywhere a normal unit can go, ignoring
// obstructions (i.e. on passable land, and not too deep in the water)
passClassName = "default-terrain-only";
break;
case "land-shore":
// 'default-terrain-only' is everywhere a normal unit can go, ignoring
// obstructions (i.e. on passable land, and not too deep in the water)
passClassName = "default-terrain-only";
break;
case "land":
default:
passClassName = "building-land";
case "land":
default:
passClassName = "building-land";
}
var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
@ -137,38 +141,30 @@ BuildRestrictions.prototype.CheckPlacement = function () {
return result; // Fail
if (this.template.Category == "Wall") {
// allow walls to be built on top of trees
let collisions = cmpObstruction.GetEntitiesBlockingConstruction();
let areWoodEnts = collisions.length > 0;
for (let ent of collisions) {
// don't let entities with unitmotion prevent us from laying a foundation
let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
let cmpResource = Engine.QueryInterface(ent, IID_ResourceSupply);
if ((!cmpResource || cmpResource.GetType().generic !== "wood") && !cmpUnitMotion) {
areWoodEnts = false;
break;
}
}
if (this.template.Category == "Wall")
{
// for walls, only test the center point
var ret = areWoodEnts ? "success" : cmpObstruction.CheckFoundation(passClassName, true);
var ret = cmpObstruction.CheckFoundation(passClassName, true);
}
else {
else
{
var ret = cmpObstruction.CheckFoundation(passClassName, false);
}
if (ret != "success") {
switch (ret) {
case "fail_error":
case "fail_no_obstruction":
error("CheckPlacement: Error returned from CheckFoundation");
break;
case "fail_obstructs_foundation":
result.message = markForTranslation("%(name)s cannot be built on another building or resource");
break;
case "fail_terrain_class":
// TODO: be more specific and/or list valid terrain?
result.message = markForTranslation("%(name)s cannot be built on invalid terrain");
if (ret != "success")
{
switch (ret)
{
case "fail_error":
case "fail_no_obstruction":
error("CheckPlacement: Error returned from CheckFoundation");
break;
case "fail_obstructs_foundation":
result.message = markForTranslation("%(name)s cannot be built on another building or resource");
break;
case "fail_terrain_class":
// TODO: be more specific and/or list valid terrain?
result.message = markForTranslation("%(name)s cannot be built on invalid terrain");
}
return result; // Fail
}
@ -187,7 +183,8 @@ BuildRestrictions.prototype.CheckPlacement = function () {
var isNeutral = tileOwner == 0;
var invalidTerritory = "";
if (isOwn) {
if (isOwn)
{
if (!this.HasTerritory("own"))
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
invalidTerritory = markForTranslationWithContext("Territory type", "own");
@ -195,7 +192,8 @@ BuildRestrictions.prototype.CheckPlacement = function () {
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
invalidTerritory = markForTranslationWithContext("Territory type", "unconnected own");
}
else if (isMutualAlly) {
else if (isMutualAlly)
{
if (!this.HasTerritory("ally"))
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
invalidTerritory = markForTranslationWithContext("Territory type", "allied");
@ -203,19 +201,22 @@ BuildRestrictions.prototype.CheckPlacement = function () {
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
invalidTerritory = markForTranslationWithContext("Territory type", "unconnected allied");
}
else if (isNeutral) {
else if (isNeutral)
{
if (!this.HasTerritory("neutral"))
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
invalidTerritory = markForTranslationWithContext("Territory type", "neutral");
}
else {
else
{
// consider everything else enemy territory
if (!this.HasTerritory("enemy"))
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
invalidTerritory = markForTranslationWithContext("Territory type", "enemy");
}
if (invalidTerritory) {
if (invalidTerritory)
{
result.message = markForTranslation("%(name)s cannot be built in %(territoryType)s territory. Valid territories: %(validTerritories)s");
result.translateParameters.push("territoryType");
result.translateParameters.push("validTerritories");
@ -226,8 +227,10 @@ BuildRestrictions.prototype.CheckPlacement = function () {
}
// Check special requirements
if (this.template.PlacementType == "shore") {
if (!cmpObstruction.CheckShorePlacement()) {
if (this.template.PlacementType == "shore")
{
if (!cmpObstruction.CheckShorePlacement())
{
result.message = markForTranslation("%(name)s must be built on a valid shoreline");
return result; // Fail
}
@ -239,18 +242,22 @@ BuildRestrictions.prototype.CheckPlacement = function () {
let template = cmpTemplateManager.GetTemplate(removeFiltersFromTemplateName(templateName));
// Check distance restriction
if (this.template.Distance) {
if (this.template.Distance)
{
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var cat = this.template.Distance.FromClass;
var filter = function (id) {
var filter = function(id)
{
var cmpIdentity = Engine.QueryInterface(id, IID_Identity);
return cmpIdentity.GetClassesList().indexOf(cat) > -1;
};
if (this.template.Distance.MinDistance !== undefined) {
if (this.template.Distance.MinDistance !== undefined)
{
let minDistance = ApplyValueModificationsToTemplate("BuildRestrictions/Distance/MinDistance", +this.template.Distance.MinDistance, cmpPlayer.GetPlayerID(), template);
if (cmpRangeManager.ExecuteQuery(this.entity, 0, minDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter)) {
if (cmpRangeManager.ExecuteQuery(this.entity, 0, minDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter))
{
let result = markForPluralTranslation(
"%(name)s too close to a %(category)s, must be at least %(distance)s meter away",
"%(name)s too close to a %(category)s, must be at least %(distance)s meters away",
@ -267,9 +274,11 @@ BuildRestrictions.prototype.CheckPlacement = function () {
return result; // Fail
}
}
if (this.template.Distance.MaxDistance !== undefined) {
if (this.template.Distance.MaxDistance !== undefined)
{
let maxDistance = ApplyValueModificationsToTemplate("BuildRestrictions/Distance/MaxDistance", +this.template.Distance.MaxDistance, cmpPlayer.GetPlayerID(), template);
if (!cmpRangeManager.ExecuteQuery(this.entity, 0, maxDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter)) {
if (!cmpRangeManager.ExecuteQuery(this.entity, 0, maxDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter))
{
let result = markForPluralTranslation(
"%(name)s too far from a %(category)s, must be within %(distance)s meter",
"%(name)s too far from a %(category)s, must be within %(distance)s meters",
@ -294,11 +303,6 @@ BuildRestrictions.prototype.CheckPlacement = function () {
return result;
};
BuildRestrictions.prototype.IsWoodEntity = function (entity) {
let cmpResource = Engine.QueryInterface(entity, IID_ResourceSupply);
return cmpResource && cmpResource.GetType().generic === "wood";
}
BuildRestrictions.prototype.GetCategory = function()
{
return this.template.Category;

View File

@ -5,7 +5,8 @@ Foundation.prototype.Schema =
"<ref name='nonNegativeDecimal'/>" +
"</element>";
Foundation.prototype.Init = function () {
Foundation.prototype.Init = function()
{
// Foundations are initially 'uncommitted' and do not block unit movement at all
// (to prevent players exploiting free foundations to confuse enemy units).
// The first builder to reach the uncommitted foundation will tell friendly units
@ -20,7 +21,6 @@ Foundation.prototype.Init = function () {
this.buildTimeModifier = +this.template.BuildTimeModifier;
this.previewEntity = INVALID_ENTITY;
this.entsToDestroy;
};
Foundation.prototype.Serialize = function()
@ -101,12 +101,9 @@ Foundation.prototype.GetNumBuilders = function()
return this.builders.size;
};
Foundation.prototype.IsFinished = function () {
if (this.GetBuildProgress() == 1.0 && this.entsToDestroy)
for (let ent of this.entsToDestroy)
Engine.DestroyEntity(ent);
return this.GetBuildProgress() == 1.0;
Foundation.prototype.IsFinished = function()
{
return (this.GetBuildProgress() == 1.0);
};
Foundation.prototype.OnOwnershipChanged = function(msg)
@ -249,18 +246,22 @@ Foundation.prototype.GetBuildTime = function()
/**
* @return {boolean} - Whether the foundation has been committed sucessfully.
*/
Foundation.prototype.Commit = function () {
Foundation.prototype.Commit = function()
{
if (this.committed)
return false;
let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
if (cmpObstruction && cmpObstruction.GetBlockMovementFlag(true)) {
this.entsToDestroy = cmpObstruction.GetEntitiesDeletedUponConstruction();
if (cmpObstruction && cmpObstruction.GetBlockMovementFlag(true))
{
for (let ent of cmpObstruction.GetEntitiesDeletedUponConstruction())
Engine.DestroyEntity(ent);
let collisions = cmpObstruction.GetEntitiesBlockingConstruction();
if (collisions.length) {
for (let ent of collisions) {
if (collisions.length)
{
for (let ent of collisions)
{
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
if (cmpUnitAI)
cmpUnitAI.LeaveFoundation(this.entity);
@ -272,10 +273,10 @@ Foundation.prototype.Commit = function () {
// animation to indicate they're waiting for people to get
// out the way
return false;
}
}
// The obstruction always blocks new foundations/construction,
// but we've temporarily allowed units to walk all over it
// (via CCmpTemplateManager). Now we need to remove that temporary

View File

@ -4,7 +4,7 @@
<Rank>Elite</Rank>
</Identity>
<Promotion>
<RequiredXp>200</RequiredXp>
<RequiredXp>400</RequiredXp>
<Entity>units/athen/champion_infantry</Entity>
</Promotion>
<VisualActor>