Ticket #3697: trade-v2.3.patch
File trade-v2.3.patch, 55.6 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
245 245 if (template.UnitMotion.Run) 246 246 ret.speed.run = func("UnitMotion/Run/Speed", +template.UnitMotion.Run.Speed, player, template); 247 247 } 248 249 if (template.Trader)250 {251 ret.trader = {252 "GainMultiplier": func("Trader/GainMultiplier", +template.Trader.GainMultiplier, player, template)253 };254 }255 248 256 249 if (template.WallSet) 257 250 { -
binaries/data/mods/public/gui/session/unit_actions.js
320 320 case "is first": 321 321 tooltip = translate("Origin trade market."); 322 322 if (tradingDetails.hasBothMarkets) 323 tooltip += "\n" + sprintf(translate("Gain: %(gain)s"), { 324 gain: getTradingTooltip(tradingDetails.gain) 325 }); 323 tooltip += "\n" + getTradeRouteTooltip(tradingDetails.gain); 326 324 else 327 325 tooltip += "\n" + translate("Right-click on another market to set it as a destination trade market."); 328 326 break; 329 327 case "is second": 330 tooltip = translate("Destination trade market.") + "\n" + sprintf(translate("Gain: %(gain)s"), { 331 gain: getTradingTooltip(tradingDetails.gain) 332 }); 328 tooltip = translate("Destination trade market.") + "\n" + getTradeRouteTooltip(tradingDetails.gain); 333 329 break; 334 330 case "set first": 335 331 tooltip = translate("Right-click to set as origin trade market"); 336 332 break; 337 333 case "set second": 338 if (tradingDetails.gain.traderGain == 0) // markets too close 339 return false; 340 tooltip = translate("Right-click to set as destination trade market.") + "\n" + sprintf(translate("Gain: %(gain)s"), { 341 gain: getTradingTooltip(tradingDetails.gain) 342 }); 334 tooltip = translate("Right-click to set as destination trade market.") + "\n" + getTradeRouteTooltip(tradingDetails.gain); 343 335 break; 344 336 } 345 return {"possible": true, "tooltip": tooltip}; 346 337 if (tradingDetails.gain !== undefined && tradingDetails.gain == 0) 338 return { "possible": false, "tooltip": translate("This market is too close to trade with.") }; 339 else 340 return { "possible": true, "tooltip": tooltip }; 347 341 }, 348 342 "actionCheck": function(target) 349 343 { 350 344 var actionInfo = getActionInfo("setup-trade-route", target); 351 if (!actionInfo.possible) 345 346 if (!actionInfo.possible && actionInfo.tooltip != undefined) 347 return { "type": "none", "cursor": "cursor-no", "tooltip": actionInfo.tooltip, "target": null }; 348 else if (!actionInfo.possible) 352 349 return false; 353 return { "type": "setup-trade-route", "cursor": "action-setup-trade-route", "tooltip": actionInfo.tooltip, "target": target};350 return { "type": "setup-trade-route", "cursor": "action-setup-trade-route", "tooltip": actionInfo.tooltip, "target": target }; 354 351 }, 355 352 "specificness": 0, 356 353 }, … … 500 497 cursor = "action-attack-move"; 501 498 } 502 499 503 if (targetState.garrisonHolder && playerCheck(entState, targetState, ["Player", "Ally"])) 500 if (hasClass(entState, "Market") && hasClass(targetState, "Market") && entState.id != targetState.id && 501 (!hasClass(entState, "NavalMarket") || hasClass(targetState, "NavalMarket")) && !playerCheck(entState, targetState, ["Enemy"])) 504 502 { 503 // Find a trader (if any) that this building can produce. 504 let trader; 505 if (entState.production && entState.production.entities.length) 506 for (let i = 0; i < entState.production.entities.length; ++i) 507 if ((trader = GetTemplateData(entState.production.entities[i]).trader)) 508 break; 509 let traderData = { "firstMarket": entState.id, "secondMarket": targetState.id, "tradeOwner": entState.player }; 510 let gain = Engine.GuiInterfaceCall("GetTradingRouteGain", traderData); 511 if (gain) 512 { 513 data.command = "trade"; 514 data.target = traderData.secondMarket; 515 data.source = traderData.firstMarket; 516 cursor = "action-setup-trade-route"; 517 tooltip = translate("Right-click to establish a default route for new traders."); 518 } 519 } 520 else if (targetState.garrisonHolder && playerCheck(entState, targetState, ["Player", "Ally"])) 521 { 505 522 data.command = "garrison"; 506 523 data.target = targetState.id; 507 524 cursor = "action-garrison"; … … 523 540 data.resourceType = resourceType; 524 541 data.resourceTemplate = targetState.template; 525 542 } 526 else if (hasClass(entState, "Market") && hasClass(targetState, "Market") && entState.id != targetState.id &&527 (!hasClass(entState, "NavalMarket") || hasClass(targetState, "NavalMarket")) && !playerCheck(entState, targetState, ["Enemy"]))528 {529 // Find a trader (if any) that this building can produce.530 var trader;531 if (entState.production && entState.production.entities.length)532 for (var i = 0; i < entState.production.entities.length; ++i)533 if ((trader = GetTemplateData(entState.production.entities[i]).trader))534 break;535 536 var traderData = { "firstMarket": entState.id, "secondMarket": targetState.id, "template": trader };537 var gain = Engine.GuiInterfaceCall("GetTradingRouteGain", traderData);538 if (gain && gain.traderGain)539 {540 data.command = "trade";541 data.target = traderData.secondMarket;542 data.source = traderData.firstMarket;543 cursor = "action-setup-trade-route";544 tooltip = translate("Right-click to establish a default route for new traders.");545 if (trader)546 tooltip += "\n" + sprintf(translate("Gain: %(gain)s"), { gain: getTradingTooltip(gain) });547 else // Foundation or cannot produce traders548 tooltip += "\n" + sprintf(translate("Expected gain: %(gain)s"), { gain: getTradingTooltip(gain) });549 }550 }551 543 else if (targetState.foundation && playerCheck(entState, targetState, ["Ally"])) 552 544 { 553 545 data.command = "build"; -
binaries/data/mods/public/gui/session/utility_functions.js
82 82 /** 83 83 * Returns a message with the details of the trade gain. 84 84 */ 85 function getTrad ingTooltip(gain)85 function getTradeRouteTooltip(gain) 86 86 { 87 return translate("This trader is currently trading between two markets, creating wealth for his empire.") + "\n" + 88 sprintf(translate("The basic value of this trade route is %(value).1f."), { value:gain }); 89 } 87 90 88 var playerID = Engine.GetPlayerID(); 89 var simState = GetSimState(); 90 91 var gainString = gain.traderGain; 92 if (gain.market1Gain && gain.market1Owner == gain.traderOwner) 93 gainString += translate("+") + gain.market1Gain; 94 if (gain.market2Gain && gain.market2Owner == gain.traderOwner) 95 gainString += translate("+") + gain.market2Gain; 96 97 var tooltip = sprintf(translate("%(gain)s (%(player)s)"), { 98 gain: gainString, 99 player: (!g_IsNetworked && gain.traderOwner == playerID) ? translate("You") : simState.players[gain.traderOwner].name 100 }); 101 102 if (gain.market1Gain && gain.market1Owner != gain.traderOwner) 103 tooltip += translateWithContext("Separation mark in an enumeration", ", ") + sprintf(translate("%(gain)s (%(player)s)"), { 104 gain: gain.market1Gain, 105 player: (!g_IsNetworked && gain.market1Owner == playerID) ? translate("You") : simState.players[gain.market1Owner].name 106 }); 107 if (gain.market2Gain && gain.market2Owner != gain.traderOwner) 108 tooltip += translateWithContext("Separation mark in an enumeration", ", ") + sprintf(translate("%(gain)s (%(player)s)"), { 109 gain: gain.market2Gain, 110 player: (!g_IsNetworked && gain.market2Owner == playerID) ? translate("You") : simState.players[gain.market2Owner].name 111 }); 112 91 /** 92 * Returns a message with the details of the trade income. 93 */ 94 function getTradeIncomeTooltip(details) 95 { 96 if (details.connections.length === 0) 97 return translate("This market has no trade routes to other markets. Use traders to connect markets together and create wealth."); 98 let avgStrength = 0.0; 99 for (let connection in details.connections) 100 avgStrength += Math.min(100, details.connections[connection].strength); 101 avgStrength /= details.connections.length; 102 let tooltip = sprintf("[font=\"sans-bold-13\"]" + translate("Trade income") + ":[/font] " + translate("%(tradeRate).1f") + " [font=\"sans-10\"]" + translate("resources/minute") + "[/font]" + 103 "\n[font=\"sans-bold-13\"]" + translate("Efficiency") + ":[/font] " + translate("%(tradeEfficiency)u%%") + 104 "\n[font=\"sans-bold-13\"]" + translate("Number of trade routes") + ":[/font] " + translate("%(MarketConnection)s") + 105 "\n[font=\"sans-bold-13\"]" + translate("Average trade route effiency") + ":[/font] " + translate("%(ConnectionStrenght)s%%"), 106 { "tradeRate" : details.rate*60.0, "tradeEfficiency": details.rawRate*100, "MarketConnection": details.connections.length, "ConnectionStrenght": avgStrength }); 107 tooltip += "[font=\"sans-10\"]\n\n" + translate("To increase your income, garrison workers until the efficiency is 100% and trade with more markets, or reinforce your current trade lines' strengths.") + "[/font]"; 113 108 return tooltip; 114 109 } 115 110 -
binaries/data/mods/public/gui/session/selection_panels.js
947 947 else 948 948 data.carried[carrying.type] = carrying.amount; 949 949 } 950 951 if (state.trader && state.trader.goods && state.trader.goods.amount)952 {953 if (!data.carried)954 data.carried = {};955 var amount = state.trader.goods.amount;956 var type = state.trader.goods.type;957 var totalGain = amount.traderGain;958 if (amount.market1Gain)959 totalGain += amount.market1Gain;960 if (amount.market2Gain)961 totalGain += amount.market2Gain;962 if (data.carried[type])963 data.carried[type] += totalGain;964 else965 data.carried[type] = totalGain;966 }967 950 } 968 951 return true; 969 952 }, -
binaries/data/mods/public/gui/session/selection_details.js
175 175 Engine.GetGUIObjectByName("resourceCarryingText").caption = sprintf(translate("%(amount)s / %(max)s"), { "amount": carried.amount, "max": carried.max }); 176 176 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = ""; 177 177 } 178 // Use the same indicators for traders 179 else if (entState.trader && entState.trader.goods.amount) 180 { 181 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 182 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 183 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/resources/"+entState.trader.goods.type+".png"; 184 let totalGain = entState.trader.goods.amount.traderGain; 185 if (entState.trader.goods.amount.market1Gain) 186 totalGain += entState.trader.goods.amount.market1Gain; 187 if (entState.trader.goods.amount.market2Gain) 188 totalGain += entState.trader.goods.amount.market2Gain; 189 Engine.GetGUIObjectByName("resourceCarryingText").caption = totalGain; 190 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("Gain: %(gain)s"), { "gain": getTradingTooltip(entState.trader.goods.amount) }); 191 } 178 // Use the same indicators for traders 179 else if (entState.trader && entState.trader.trading) 180 { 181 var nextMarket = GetEntityState(entState.trader.nextMarket); 182 // Find the connection if it exists. 183 let connection = null; 184 if (nextMarket && nextMarket.tradeIncome) 185 connection = nextMarket.tradeIncome.connections.find(conn => conn[0] === entState.trader.originMarket); 186 187 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 188 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 189 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/trade.png"; 190 Engine.GetGUIObjectByName("resourceCarryingText").caption = ""; 191 if (connection) 192 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("This trader is part of a trade route between two markets.\nThis route's efficiency is %(strength)u%%."), { "strength": Math.min(connection[1], 100) }); 193 else 194 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = translate("This trader is trying to establish a trade route, but the number of traders isn't high enough for this trade route to be viable yet."); 195 } 192 196 // And for number of workers 193 197 else if (entState.foundation && entState.visibility == "visible") 194 198 { … … 206 210 else 207 211 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = translate("Number of builders."); 208 212 } 213 // And for the trade income icon 214 else if (entState.tradeIncome) 215 { 216 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 217 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 218 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/trade.png"; 219 Engine.GetGUIObjectByName("resourceCarryingText").caption = sprintf(translate("%(MarketEfficiency)u%% x%(NumberOfConnections)u"), 220 { "MarketEfficiency": +entState.tradeIncome.rawRate*100, 221 "NumberOfConnections": entState.tradeIncome.connections.length }); 222 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = getTradeIncomeTooltip(entState.tradeIncome); 223 } 209 224 else if (entState.repairable && entState.repairable.numBuilders > 0 && entState.visibility == "visible") 210 225 { 211 226 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; -
binaries/data/mods/public/shaders/glsl/water_high.fs
127 127 } 128 128 129 129 void main() 130 { 131 //gl_FragColor = texture2D(waterEffectsTex, gl_FragCoord.xy/screenSize); 132 //return; 133 130 { 134 131 float fresnel; 135 float t; // Temporary variable136 132 vec2 reflCoords, refrCoords; 137 133 vec3 reflColor, refrColor, specular; 138 float losMod ;134 float losMod, reflMod; 139 135 140 vec3 l = -sunDir;141 136 vec3 v = normalize(cameraPos - worldPos); 142 vec3 h = normalize(l + v);143 137 144 138 // Calculate water normals. 145 139 … … 147 141 float baseScale = waveParams1.g; 148 142 float flattenism = waveParams1.b; 149 143 float baseBump = waveParams1.a; 150 151 float smallIntensity = waveParams2.r;152 float smallBase = waveParams2.g;153 144 float BigMovement = waveParams2.b; 154 float SmallMovement = waveParams2.a;155 145 156 146 float moddedTime = mod(time * 60.0, 8.0) / 8.0; 157 147 158 148 // This method uses 60 animated water frames. We're blending between each two frames 159 // TODO: could probably have fewer frames thanks to this blending.160 149 // Scale the normal textures by waviness so that big waviness means bigger waves. 161 150 vec3 ww1 = texture2D(normalMap, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).xzy; 162 151 vec3 ww2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).xzy; … … 165 154 ww1.x = wwInterp.x * WindCosSin.x - wwInterp.z * WindCosSin.y; 166 155 ww1.z = wwInterp.x * WindCosSin.y + wwInterp.z * WindCosSin.x; 167 156 ww1.y = wwInterp.y; 168 169 vec3 smallWW = texture2D(normalMap, (normalCoords.st + normalCoords.zw * SmallMovement*waviness/10.0) * baseScale*3.0).xzy;170 vec3 smallWW2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * SmallMovement*waviness/10.0) * baseScale*3.0).xzy;171 vec3 smallWWInterp = mix(smallWW, smallWW2, moddedTime) - vec3(0.5,0.0,0.5);172 173 smallWW.x = smallWWInterp.x * WindCosSin.x - smallWWInterp.z * WindCosSin.y;174 smallWW.z = smallWWInterp.x * WindCosSin.y + smallWWInterp.z * WindCosSin.x;175 smallWW.y = smallWWInterp.y;176 177 ww1 += vec3(smallWW)*(fwaviness/10.0*smallIntensity + smallBase);178 179 ww1 = mix(smallWW, ww1, waterInfo.r);180 157 181 158 // Flatten them based on waviness. 182 159 vec3 n = normalize(mix(vec3(0.0,1.0,0.0),ww1, clamp(baseBump + fwaviness/flattenism,0.0,1.0))); … … 189 166 n = mix(vec3(0.0,1.0,0.0), n,0.5 + waterInfo.r/2.0); 190 167 #endif 191 168 192 n = vec3(-n.x,n.y,-n.z); 169 n = vec3(-n.x,n.y,-n.z); // the final wave normal vector 193 170 194 // simulates how parallel the "point->sun", "view->point" vectors are.195 float ndoth = dot(n , h);196 197 171 // how perpendicular to the normal our view is. Used for fresnel. 198 172 float ndotv = clamp(dot(n, v),0.0,1.0); 199 173 200 // diffuse lighting-like. used for shadows?201 f loat ndotl = (dot(n, l) + 1.0)/2.0;174 // Fresnel for "how much reflection vs how much refraction". 175 fresnel = clamp(((pow(1.1 - ndotv, 3.0)) * 1.5), 0.2, 1.0); // approximation. I'm using 1.1 and not 1.0 because it causes artifacts, see #1714 202 176 177 // Specular lighting vectors 178 vec3 specVector = normalize(reflect(sunDir, n)); 179 float specIntensity = pow(abs(dot(specVector, v)), 256.0); 180 181 specular = specIntensity * sunColor * fresnel; 182 203 183 float depth; 204 184 #if USE_REAL_DEPTH 205 185 // Don't change these two. They should match the values in the config (TODO: dec uniforms). … … 238 218 depth = max(depth,fancyeffects.a); 239 219 #endif 240 220 241 // Fresnel for "how much reflection vs how much refraction".242 // Since we're not trying to simulate a realistic ocean 100%, aim for something that gives a little too much reflection243 // because we're not used to seeing the see from above.244 fresnel = clamp(pow(1.05 - ndotv, 1.1),0.0,0.8); // approximation. I'm using 1.05 and not 1.0 because it causes artifacts, see #1714245 // multiply by v.y so that in the distance refraction wins.246 // TODO: this is a hack because reflections don't work in the distance.247 fresnel = clamp(fresnel*1.5,0.0,0.9);248 fresnel *= min(1.0,log(1.0 + v.y*5.0));249 250 //gl_FragColor = vec4(fresnel,fresnel,fresnel,1.0);251 //return;252 253 221 #if USE_SHADOWS_ON_WATER && USE_SHADOW 254 222 float shadow = get_shadow(vec4(v_shadow.xy, v_shadow.zw)); 255 223 #endif … … 256 224 257 225 // for refraction, we want to adjust the value by v.y slightly otherwise it gets too different between "from above" and "from the sides". 258 226 // And it looks weird (again, we are not used to seeing water from above). 259 float fixedVy = max(v.y,0. 1);227 float fixedVy = max(v.y,0.01); 260 228 261 float distoFactor = clamp(depth/2.0,0.0,7.0); 229 // This somewhat complicated function controls the amount of distortion relative to depth. 230 // The overall function is depth/log(depth) to keep distortions from becoming too extreme, 231 // and the extra terms modify the amount of refraction and prevent numerical singularities from 232 // causing artifacts. 233 float distoFactor = (1.5 * depth) / max(1.0, 2.0 * log(1.0 + depth)); 262 234 263 235 float murky = mix(200.0,0.1,pow(murkiness,0.25)); 264 236 265 237 #if USE_REFRACTION 266 refrCoords = clamp( (0.5*refractionCoords.xy - n.xz * distoFactor*7.0) / refractionCoords.z + 0.5,0.0,1.0); // Unbias texture coords 238 // distort the texture coords under where the water is to simulate refraction. 239 refrCoords = (0.5 * refractionCoords.xy - n.xz * distoFactor) / refractionCoords.z + 0.5; 267 240 vec3 refColor = texture2D(refractionMap, refrCoords).rgb; 268 if (refColor.r > refColor.g + refColor.b + 0.25) 241 242 // note, the refraction map is cleared using (255, 0, 0), so pixels outside of the water plane are pure red. 243 // if we get a pure red fragment, use an undistorted/less distorted coord instead. 244 if (refColor.r == 1.0) 269 245 { 270 refrCoords = clamp( (0.5*refractionCoords.xy + n.xz) / refractionCoords.z + 0.5,0.0,1.0); // Unbias texture coords246 refrCoords = (0.5*refractionCoords.xy + n.xz) / refractionCoords.z + 0.5; 271 247 refColor = texture2D(refractionMap, refrCoords).rgb; 272 248 } 273 249 274 // TODO: make murkiness (both types rematter on that. 275 // linearly extinct the water. This is how quickly we see nothing but the pure water color 250 // Apply water tint and murk color. 276 251 float extFact = max(0.0,1.0 - (depth*fixedVy/murky)); 277 // This is how tinted the water is, ie how quickly the refracted floor takes the tint of the water278 252 float ColextFact = max(0.0,1.0 - (depth*fixedVy/murky)); 279 253 vec3 colll = mix(refColor*tint,refColor,ColextFact); 280 254 281 #if USE_SHADOWS_ON_WATER && USE_SHADOW282 // TODO:283 255 refrColor = mix(color, colll, extFact); 284 256 #else 257 // Apply water tint and murk color only. 258 float extFact = max(0.0,1.0 - (depth*fixedVy/murky)); 259 float ColextFact = max(0.0,1.0 - (depth*fixedVy/murky)); 260 vec3 colll = mix(refColor*tint,refColor,ColextFact); 261 285 262 refrColor = mix(color, colll, extFact); 286 263 #endif 287 #else288 // linearly extinct the water. This is how quickly we see nothing but the pure water color289 float extFact = max(0.0,1.0 - (depth*fixedVy/20.0));290 // using both those factors, get our transparency.291 // This will be our base transparency on top.292 float base = 0.4 + depth*fixedVy/15.0; // TODO: murkiness.293 float alphaCoeff = mix(1.0, base, extFact);294 refrColor = color;295 #endif296 264 297 265 #if USE_REFLECTION 298 266 // Reflections 299 267 // we use real reflections against th skybox, and distort a texture of objects closer. 300 268 vec3 eye = reflect(v,n); 301 //eye.y = min(-0.2,eye.y);269 302 270 // let's calculate where we intersect with the skycube. 303 271 Ray myRay = Ray(vec3(worldPos.x/4.0,worldPos.y,worldPos.z/4.0),eye); 304 272 vec3 start = vec3(-1500.0 + mapSize/2.0,-100.0,-1500.0 + mapSize/2.0); … … 309 277 newpos *= skyBoxRot; 310 278 newpos.y *= 4.0; 311 279 reflColor = textureCube(skyCube, newpos.rgb).rgb; 312 313 // Reflections appear more distorted when viewed from a lower angle. Simulate this.314 float angleEffect = clamp(1.3 - dot(vec3(0.0,1.0,0.0), v),0.0,1.0);315 280 316 reflCoords = clamp( (0.5*reflectionCoords.xy - 40.0 * n.zx * angleEffect) / reflectionCoords.z + 0.5,0.0,1.0); // Unbias texture coords 281 // Distort the reflection coords based on waves. 282 reflCoords = (0.5*reflectionCoords.xy - 15.0 * n.zx) / reflectionCoords.z + 0.5; 317 283 vec4 refTex = texture2D(reflectionMap, reflCoords); 318 fresnel = clamp(fresnel+refTex.a/3.0,0.0,1.0);319 reflColor = refTex.rgb * refTex.a + reflColor*(1.0-refTex.a);320 284 285 // interpolate between the sky color and nearby objects. 286 reflColor = mix(reflColor.rgb, refTex.rgb, refTex.a); 287 // reflMod is used to reduce the intensity of sky reflections, which otherwise are too extreme. 288 reflMod = max(refTex.a, 0.55); 289 321 290 #else 322 // Temp fix for some ATI cards (see irc logs on th 1st of august betwee, fexor and wraitii)323 //reflCoords = clamp( (0.5*reflectionCoords.xy - waviness * mix(1.0, 20.0,waviness/10.0) * n.zx) / reflectionCoords.z + 0.5,0.0,1.0); // Unbias texture coords324 //vec3 refTex = texture2D(reflectionMap, reflCoords).rgb;325 //reflColor = refTex.rgb;326 291 reflColor = vec3(0.15, 0.7, 0.82); 327 292 #endif 328 293 329 // TODO: At very low angles the reflection stuff doesn't really work any more:330 // IRL you would get a blur of the sky, but we don't have that precision (would require mad oversampling)331 // So tend towards a predefined color (per-map) which looks like what the skybox would look like if you really blurred it.332 // The TODO here would be to precompute a band (1x32?) that represents the average color around the map.333 // TODO: another issue is that at high distances (half map) the texture blurs into flatness. Using better mipmaps won't really solve it334 // So we'll need to stop showing reflections and default to sky color there too.335 // Unless maybe waviness is so low that you would see like in a mirror anyways.336 //float disttt = distance(worldPos,cameraPos);337 //reflColor = mix(vec3(0.5,0.5,0.55), reflColor, clamp(1.0-disttt/600.0*disttt/600.0,0.0,1.0));//clamp(-0.05 + v.y*20.0,0.0,1.0));338 339 // Specular.340 specular = pow(ndoth, mix(5.0,2000.0, clamp(v.y*v.y*2.0,0.0,1.0)))*sunColor * 1.5;// * sunColor * 1.5 * ww.r;341 342 294 losMod = texture2D(losMap, losCoords.st).a; 343 295 losMod = losMod < 0.03 ? 0.0 : losMod; 344 296 345 float wavesFresnel = 1.0;346 347 #if USE_FANCY_EFFECTS348 wavesFresnel = mix(1.0-fancyeffects.a,1.0,clamp(depth,0.0,1.0));349 #endif350 351 297 vec3 color; 352 298 #if USE_SHADOWS_ON_WATER && USE_SHADOW 353 299 float fresShadow = mix(fresnel, fresnel*shadow, 0.05 + murkiness*0.2); 354 color = mix(refrColor, reflColor, fresShadow * wavesFresnel);300 color = mix(refrColor, reflColor, fresShadow); 355 301 #else 356 color = mix(refrColor, reflColor, fresnel * wavesFresnel);302 color = mix(refrColor, reflColor, fresnel * reflMod); 357 303 #endif 358 304 359 305 #if USE_SHADOWS_ON_WATER && USE_SHADOW … … 373 319 foaminterp *= mix(foam3, foam4, moddedTime); 374 320 375 321 foam1.x = foaminterp.x * WindCosSin.x - foaminterp.z * WindCosSin.y; 376 //foam1.z = foaminterp.x * WindCosSin.y + foaminterp.z * WindCosSin.x; 377 //foam1.y = foaminterp.y; 322 378 323 float foam = FoamEffects.r * FoamEffects.a*0.4 + pow(foam1.x*(5.0+waviness),(2.6 - waviness/5.5)); 379 foam *= ndotl;380 324 381 gl_FragColor.rgb = get_fog(color) * losMod + foam * losMod; // + fancyeffects.a * losMod;325 gl_FragColor.rgb = get_fog(color) * losMod + foam * losMod; 382 326 #else 383 327 gl_FragColor.rgb = get_fog(color) * losMod; 384 328 #endif … … 393 337 if (fancyeffects.a < 0.05 && waterDepth < -1.0 ) 394 338 gl_FragColor.a = 0.0; 395 339 #endif 396 397 //gl_FragColor = vec4(sunColor,1.0);398 340 } -
binaries/data/mods/public/simulation/components/TradeIncome.js
1 // Array of resource names 2 const RESOURCES = ["food", "wood", "stone", "metal"]; 3 4 function TradeIncome() {} 5 6 TradeIncome.prototype.Schema = 7 "<a:help>Lets the unit generate resources that traders could pick up and trade with.</a:help>" + 8 "<a:example>" + 9 "<Efficiency>2.0</Efficiency>" + 10 "</a:example>" + 11 "<element name='Efficiency' a:help='Gives the basic trade income of the building at full efficiency.'>" + 12 "<ref name='positiveDecimal'/>" + 13 "</element>" + 14 "<optional>" + 15 "<element name='GarrisonEfficiency' a:help='Improve the efficiency of trading by garrisoning units inside'>" + 16 "<optional>" + 17 "<element name='Classes' a:help='only units having one of the following classes will be considered. Defaults to all.'>" + 18 "<text/>" + 19 "</element>" + 20 "</optional>" + 21 "<element name='EmptyEfficiency' a:help='Basic efficiency when empty. Expressed as a % of total efficiency.'>" + 22 "<ref name='nonNegativeDecimal'/>" + 23 "</element>" + 24 "<optional>" + 25 "<element name='MaxGarrison' a:help='Number of unit required for efficiency to be 100%. Defaults to \"garrison capacity\"'>" + 26 "<data type='nonNegativeInteger'/>" + 27 "</element>" + 28 "</optional>" + 29 "</element>" + 30 "</optional>"; 31 32 TradeIncome.prototype.Init = function() 33 { 34 this.connections = new Map(); 35 36 // Call the timer 37 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 38 cmpTimer.SetInterval(this.entity, IID_TradeIncome, "GenerateResources", 5000, 5000, undefined); 39 40 // Quick sanity check 41 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 42 if (this.template.GarrisonEfficiency && !cmpGarrisonHolder) 43 warn("Entity " + this.entity + " has a TradeIncome component that makes use of garrisoned entity but does not support garrisoning."); 44 }; 45 46 // When a trader arrives, it either establishes or reinforces a connection. 47 TradeIncome.prototype.AddConnectionWithMarket = function(market, amount) 48 { 49 if (!this.connections.get(market)) 50 this.connections.set(market, { "strength" : 0, "distance" : 0, "international" : false }); 51 52 this.connections.get(market).strength += +amount; 53 // Set up a maximal amount so that you can't just get enough connection strength then delete your traders. 54 if (this.connections.get(market).strength > 1000) 55 this.connections.get(market).strength = 1000; 56 57 // Reset distance. Ought not change. 58 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 59 var cmpConnectionPosition = Engine.QueryInterface(market, IID_Position); 60 if (!cmpPosition || !cmpPosition.IsInWorld() || !cmpConnectionPosition || !cmpConnectionPosition.IsInWorld()) 61 return; 62 63 var Position = cmpPosition.GetPosition2D(); 64 var ConnectionPosition = cmpConnectionPosition.GetPosition2D(); 65 var distance = Math.sqrt(Math.pow(Position.x - ConnectionPosition.x, 2) 66 + Math.pow(Position.y - ConnectionPosition.y, 2)); 67 this.connections.get(market).distance = distance; 68 69 // Reset internationality, in case the owner changes: from our POV until a trader brings news, we are selling items from a faraway land. 70 var owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 71 var ownerConnection = Engine.QueryInterface(market, IID_Ownership).GetOwner(); 72 73 this.connections.get(market).international = owner != ownerConnection; 74 } 75 76 TradeIncome.prototype.GetConnections = function() 77 { 78 var ret = []; 79 for (let conn of this.connections.keys()) 80 ret.push( 81 { 82 "marketID": conn, 83 "strength": this.connections.get(conn).strength, 84 "distance": this.connections.get(conn).distance, 85 "international": this.connections.get(conn).international 86 }); 87 return ret; 88 } 89 90 TradeIncome.prototype.GetTradeIncomeRate = function() 91 { 92 if (this.connections.size === 0) 93 return 0; 94 95 // Calculate our connection bonuses. 96 var connectionEffect = 0; 97 98 var owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 99 100 for (let i of this.connections.keys()) 101 { 102 let bonus = CalculateMarketConnectionBonusDirect(this.connections.get(i).distance, owner, this.connections.get(i).international); 103 connectionEffect += bonus * Math.min(1.0, this.connections.get(i).strength/100.0); 104 } 105 return Math.sqrt(connectionEffect) * this.GetRawEfficiency(); 106 } 107 108 TradeIncome.prototype.GetRawEfficiency = function() 109 { 110 if (!this.template.GarrisonEfficiency) 111 return 1.0; 112 113 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 114 if (!cmpGarrisonHolder) 115 return 1.0; // Probably should not happen 116 117 118 // Depending on how many units are garrisoned, modulate efficiency 119 var capacity = this.template.GarrisonEfficiency.MaxGarrison === undefined ? cmpGarrisonHolder.GetCapacity() : +this.template.GarrisonEfficiency.MaxGarrison; 120 var currentCount = this.template.GarrisonEfficiency.Classes === undefined ? 121 cmpGarrisonHolder.GetEntities().length : cmpGarrisonHolder.GetGarrisonedArcherCount(this.template.GarrisonEfficiency.Classes); 122 return 0.2 + (currentCount / capacity) * 0.8; 123 } 124 125 TradeIncome.prototype.GenerateResources = function() 126 { 127 if (this.connections.size === 0) 128 return; 129 130 var cmpPlayer = QueryOwnerInterface(this.entity); 131 if (!cmpPlayer) 132 return; 133 134 var cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 135 136 var rate = this.GetTradeIncomeRate() * 5; 137 var goods = cmpPlayer.GetTradingGoods(); 138 139 for (let good in goods) 140 { 141 if (RESOURCES.indexOf(good) === -1) 142 continue; 143 144 cmpPlayer.AddResource(good, rate * goods[good]/100.0); // Should add up to 100% 145 146 if (cmpStatisticsTracker) 147 cmpStatisticsTracker.IncreaseTradeIncomeCounter(rate * goods[good]); 148 } 149 150 for (let i of this.connections.keys()) 151 { 152 this.connections.get(i).strength -= 10; 153 if (this.connections.get(i).strength <= 0) 154 this.connections.delete(i); 155 } 156 }; 157 158 Engine.RegisterComponentType(IID_TradeIncome, "TradeIncome", TradeIncome); -
binaries/data/mods/public/simulation/components/UnitAI.js
Property changes on: binaries/data/mods/public/simulation/components/TradeIncome.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
5221 5221 5222 5222 if (this.CheckTargetRange(currentMarket, IID_Trader)) 5223 5223 { 5224 varcmpTrader = Engine.QueryInterface(this.entity, IID_Trader);5224 let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 5225 5225 cmpTrader.PerformTrade(currentMarket); 5226 if (!cmpTrader.GetGain().traderGain)5227 {5228 this.StopTrading();5229 return;5230 }5231 5226 5232 5227 if (this.order.data.route && this.order.data.route.length) 5233 5228 { -
binaries/data/mods/public/simulation/components/Trader.js
1 // See helpers/TraderGain.js for the CalculateTaderGain() function which works out how many2 // resources a trader gets3 4 // Additional gain for ships for each garrisoned trader, in percents5 const GARRISONED_TRADER_ADDITION = 20;6 7 1 // Array of resource names 8 2 const RESOURCES = ["food", "wood", "stone", "metal"]; 9 3 … … 12 6 Trader.prototype.Schema = 13 7 "<a:help>Lets the unit generate resouces while moving between markets (or docks in case of water trading).</a:help>" + 14 8 "<a:example>" + 15 "<MaxDistance>2.0</MaxDistance>" + 16 "<GainMultiplier>1.0</GainMultiplier>" + 9 "<ConnectionStrength>5.0</ConnectionStrength>" + 17 10 "</a:example>" + 18 "<element name=' GainMultiplier' a:help='Additional gain multiplier'>" +11 "<element name='ConnectionStrength' a:help='How much more connection strength a trader gives.'>" + 19 12 "<ref name='positiveDecimal'/>" + 20 13 "</element>"; 21 14 … … 23 16 { 24 17 this.firstMarket = INVALID_ENTITY; 25 18 this.secondMarket = INVALID_ENTITY; 26 // Gain from one pass between markets 27 this.gain = null; 28 // Selected resource for trading 29 this.requiredGoods = undefined; 19 30 20 // Currently carried goods 31 this.goods = { "type": null, "amount": null, "origin": null };21 this.goodsOrigin = null; 32 22 }; 33 23 34 Trader.prototype.CalculateGain = function(firstMarket, secondMarket)35 {36 var gain = CalculateTraderGain(firstMarket, secondMarket, this.template, this.entity);37 if (!gain) // One of our markets must have been destroyed38 return null;39 40 // For ship increase gain for each garrisoned trader41 // Calculate this here to save passing unnecessary stuff into the CalculateTraderGain function42 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);43 if (cmpIdentity && cmpIdentity.HasClass("Ship"))44 {45 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);46 if (cmpGarrisonHolder)47 {48 var garrisonMultiplier = 1;49 var garrisonedTradersCount = 0;50 for each (var entity in cmpGarrisonHolder.GetEntities())51 {52 var cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader);53 if (cmpGarrisonedUnitTrader)54 garrisonedTradersCount++;55 }56 garrisonMultiplier *= 1 + GARRISONED_TRADER_ADDITION * garrisonedTradersCount / 100;57 58 if (gain.traderGain)59 gain.traderGain = Math.round(garrisonMultiplier * gain.traderGain);60 if (gain.market1Gain)61 gain.market1Gain = Math.round(garrisonMultiplier * gain.market1Gain);62 if (gain.market2Gain)63 gain.market2Gain = Math.round(garrisonMultiplier * gain.market2Gain);64 }65 }66 67 return gain;68 };69 70 Trader.prototype.GetGain = function()71 {72 return this.gain;73 };74 75 24 // Set target as target market. 76 25 // Return true if at least one of markets was changed. 77 26 Trader.prototype.SetTargetMarket = function(target, source) … … 110 59 if (target == this.firstMarket) 111 60 marketsChanged = false; 112 61 else 113 {114 62 this.secondMarket = target; 115 this.gain = this.CalculateGain(this.firstMarket, this.secondMarket);116 }117 63 } 118 64 else 119 65 { … … 124 70 if (marketsChanged) 125 71 { 126 72 // Drop carried goods 127 this.goods .amount= null;73 this.goodsOrigin = null; 128 74 } 129 75 return marketsChanged; 130 76 }; … … 144 90 return this.firstMarket && this.secondMarket; 145 91 }; 146 92 147 Trader.prototype.GetRequiredGoods = function()148 {149 return this.requiredGoods;150 };151 152 Trader.prototype.SetRequiredGoods = function(requiredGoods)153 {154 // Check that argument is a correct resource name155 if (!requiredGoods || RESOURCES.indexOf(requiredGoods) == -1)156 this.requiredGoods = undefined;157 else158 this.requiredGoods = requiredGoods;159 };160 161 93 Trader.prototype.CanTrade = function(target) 162 94 { 163 95 var cmpTraderIdentity = Engine.QueryInterface(this.entity, IID_Identity); … … 186 118 187 119 Trader.prototype.PerformTrade = function(currentMarket) 188 120 { 189 if (this.goods .amount && this.goods.amount.traderGain)121 if (this.goodsOrigin && this.goodsOrigin !== currentMarket) 190 122 { 191 var cmpPlayer = QueryOwnerInterface(this.entity); 192 if (cmpPlayer) 193 cmpPlayer.AddResource(this.goods.type, this.goods.amount.traderGain); 194 195 var cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 196 if (cmpStatisticsTracker) 197 cmpStatisticsTracker.IncreaseTradeIncomeCounter(this.goods.amount.traderGain); 198 199 if (this.goods.amount.market1Gain) 200 { 201 var cmpPlayer = QueryOwnerInterface(this.firstMarket); 202 if (cmpPlayer) 203 cmpPlayer.AddResource(this.goods.type, this.goods.amount.market1Gain); 204 205 var cmpStatisticsTracker = QueryOwnerInterface(this.firstMarket, IID_StatisticsTracker); 206 if (cmpStatisticsTracker) 207 cmpStatisticsTracker.IncreaseTradeIncomeCounter(this.goods.amount.market1Gain); 208 } 209 210 if (this.goods.amount.market2Gain) 211 { 212 var cmpPlayer = QueryOwnerInterface(this.secondMarket); 213 if (cmpPlayer) 214 cmpPlayer.AddResource(this.goods.type, this.goods.amount.market2Gain); 215 216 var cmpStatisticsTracker = QueryOwnerInterface(this.secondMarket, IID_StatisticsTracker); 217 if (cmpStatisticsTracker) 218 cmpStatisticsTracker.IncreaseTradeIncomeCounter(this.goods.amount.market2Gain); 219 } 123 let cmpTradeIncome = Engine.QueryInterface(currentMarket, IID_TradeIncome); 124 if (cmpTradeIncome) 125 cmpTradeIncome.AddConnectionWithMarket(this.goodsOrigin, this.template.ConnectionStrength); 220 126 } 221 222 // First take the preferred goods of the trader if any, 223 // otherwise choose one according to the player's trading priorities 224 // if still nothing (but should never happen), choose metal 225 // and recomputes the gain in case it has changed (for example by technology) 226 var nextGoods = this.GetRequiredGoods(); 227 if (!nextGoods || RESOURCES.indexOf(nextGoods) == -1) 228 { 229 var cmpPlayer = QueryOwnerInterface(this.entity); 230 if (cmpPlayer) 231 nextGoods = cmpPlayer.GetNextTradingGoods(); 232 233 if (!nextGoods || RESOURCES.indexOf(nextGoods) == -1) 234 nextGoods = "metal"; 235 } 236 this.goods.type = nextGoods; 237 this.goods.amount = this.CalculateGain(this.firstMarket, this.secondMarket); 238 this.goods.origin = currentMarket; 127 this.goodsOrigin = +currentMarket; 239 128 }; 240 129 241 Trader.prototype.GetGoods = function()242 {243 return this.goods;244 };245 246 130 Trader.prototype.GetNextMarket = function() 247 131 { 248 if (this.goods .amount && this.goods.origin == this.firstMarket)132 if (this.goodsOrigin == this.firstMarket) 249 133 return this.secondMarket; 250 134 251 if (this.goods .amount && this.goods.origin != this.secondMarket)252 this.goods .amount = null; // leftover from previous trading135 if (this.goodsOrigin != this.secondMarket) 136 this.goodsOrigin = null; // Leftover from previous trading 253 137 return this.firstMarket; 254 138 }; 255 139 140 Trader.prototype.GetOriginMarket = function() 141 { 142 return this.GetNextMarket() == this.firstMarket ? this.secondMarket : this.firstMarket; 143 }; 144 256 145 Trader.prototype.StopTrading = function() 257 146 { 258 147 // Drop carried goods 259 this.goods .amount= null;148 this.goodsOrigin = null; 260 149 // Reset markets 261 150 this.firstMarket = INVALID_ENTITY; 262 151 this.secondMarket = INVALID_ENTITY; … … 274 163 return { "min": 0, "max": max}; 275 164 }; 276 165 277 Trader.prototype.OnGarrisonedUnitsChanged = function()278 {279 if (this.HasBothMarkets())280 this.gain = this.CalculateGain(this.firstMarket, this.secondMarket);281 };282 283 166 Engine.RegisterComponentType(IID_Trader, "Trader", Trader); -
binaries/data/mods/public/simulation/components/GuiInterface.js
240 240 "resourceCarrying": null, 241 241 "rotation": null, 242 242 "trader": null, 243 "tradeIncome": null, 243 244 "unitAI": null, 244 245 "visibility": null, 245 246 }; … … 303 304 let cmpTrader = Engine.QueryInterface(ent, IID_Trader); 304 305 if (cmpTrader) 305 306 ret.trader = { 306 "goods": cmpTrader.GetGoods(), 307 "requiredGoods": cmpTrader.GetRequiredGoods() 307 "trading": cmpTrader.HasBothMarkets(), 308 "nextMarket": cmpTrader.GetNextMarket(), 309 "originMarket": cmpTrader.GetOriginMarket() 308 310 }; 309 311 312 var cmpTradeIncome = Engine.QueryInterface(ent, IID_TradeIncome); 313 if (cmpTradeIncome) 314 ret.tradeIncome = { 315 "connections": cmpTradeIncome.GetConnections(), 316 "rate": cmpTradeIncome.GetTradeIncomeRate(), 317 "rawRate": cmpTradeIncome.GetRawEfficiency() 318 }; 319 310 320 let cmpFogging = Engine.QueryInterface(ent, IID_Fogging); 311 321 if (cmpFogging) 312 322 { … … 1646 1656 if (!data.firstMarket || !data.secondMarket) 1647 1657 return null; 1648 1658 1649 return Calculate TraderGain(data.firstMarket, data.secondMarket, data.template);1659 return CalculateMarketConnectionBonus(data.firstMarket, data.secondMarket, data.tradeOwner); 1650 1660 }; 1651 1661 1652 1662 GuiInterface.prototype.GetTradingDetails = function(player, data) … … 1665 1675 "hasBothMarkets": cmpEntityTrader.HasBothMarkets() 1666 1676 }; 1667 1677 if (cmpEntityTrader.HasBothMarkets()) 1668 result.gain = cmpEntityTrader.GetGain();1678 result.gain = CalculateMarketConnectionBonus(firstMarket, secondMarket, player) 1669 1679 } 1670 1680 else if (data.target === secondMarket) 1671 1681 { 1672 1682 result = { 1673 1683 "type": "is second", 1674 "gain": cmpEntityTrader.GetGain(),1684 "gain": CalculateMarketConnectionBonus(firstMarket, data.target, player) 1675 1685 }; 1676 1686 } 1677 1687 else if (!firstMarket) … … 1682 1692 { 1683 1693 result = { 1684 1694 "type": "set second", 1685 "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target),1695 "gain": CalculateMarketConnectionBonus(firstMarket, data.target, player) 1686 1696 }; 1687 1697 } 1688 1698 else -
binaries/data/mods/public/simulation/components/Looter.js
36 36 for (let resource of cmpResourceGatherer.GetCarryingStatus()) 37 37 resources[resource.type] += resource.amount; 38 38 39 // Loot resources traders carry40 var cmpTrader = Engine.QueryInterface(targetEntity, IID_Trader);41 if (cmpTrader)42 {43 let carriedGoods = cmpTrader.GetGoods();44 if (carriedGoods.amount)45 {46 resources[carriedGoods.type] +=47 + (carriedGoods.amount.traderGain || 0)48 + (carriedGoods.amount.market1Gain || 0)49 + (carriedGoods.amount.market2Gain || 0);50 }51 }52 53 39 // Transfer resources 54 40 var cmpPlayer = QueryOwnerInterface(this.entity); 55 41 cmpPlayer.AddResources(resources); -
binaries/data/mods/public/simulation/components/interfaces/TradeIncome.js
1 Engine.RegisterInterface("TradeIncome"); 2 No newline at end of file -
binaries/data/mods/public/simulation/helpers/TraderGain.js
Property changes on: binaries/data/mods/public/simulation/components/interfaces/TradeIncome.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 1 // This constant used to adjust gain value depending on distance 2 const DISTANCE_FACTOR = 1 / 1 15;2 const DISTANCE_FACTOR = 1 / 100.0; 3 3 4 // Additional gain (applying to each market) for trading performed between markets of different players, in percents 5 const INTERNATIONAL_TRADING_ADDITION = 25; 4 // When performing trade between markets owned by two different players, multiply trader gain by this value. 5 // When trading between your markets, you gain the resources at both markets. 6 // So this must be at least above 2 to make it interesting to trade with your ally. 7 const INTERNATIONAL_TRADING_MULTIPLICATION = 1.4; 6 8 7 // If trader undefined, the trader owner is supposed to be the same as the first market 8 function CalculateTraderGain(firstMarket, secondMarket, template, trader) 9 function CalculateMarketConnectionBonus(firstMarket, secondMarket, tradeOwner) 9 10 { 10 var gain = {};11 var bonus = 1; 11 12 12 13 var cmpFirstMarketPosition = Engine.QueryInterface(firstMarket, IID_Position); 13 14 var cmpSecondMarketPosition = Engine.QueryInterface(secondMarket, IID_Position); 14 15 if (!cmpFirstMarketPosition || !cmpFirstMarketPosition.IsInWorld() || 15 16 !cmpSecondMarketPosition || !cmpSecondMarketPosition.IsInWorld()) 16 17 return null; 17 18 var firstMarketPosition = cmpFirstMarketPosition.GetPosition2D(); 18 19 var secondMarketPosition = cmpSecondMarketPosition.GetPosition2D(); 19 20 21 20 22 // Calculate ordinary Euclidean distance between markets. 21 23 // We don't use pathfinder, because ordinary distance looks more fair. 22 var distance = firstMarketPosition.distanceTo(secondMarketPosition); 23 // We calculate gain as square of distance to encourage trading between remote markets 24 gain.traderGain = Math.pow(distance * DISTANCE_FACTOR, 2); 25 if (template && template.GainMultiplier) 26 { 27 if (trader) 28 gain.traderGain *= ApplyValueModificationsToEntity("Trader/GainMultiplier", +template.GainMultiplier, trader); 29 else // called from the gui with modifications already applied 30 gain.traderGain *= template.GainMultiplier; 31 } 32 // If trader undefined, the trader owner is supposed to be the same as the first market 33 var cmpOwnership = trader ? Engine.QueryInterface(trader, IID_Ownership) : Engine.QueryInterface(firstMarket, IID_Ownership); 34 if (!cmpOwnership) 35 return null; 36 gain.traderOwner = cmpOwnership.GetOwner(); 24 var distance = Math.sqrt(Math.pow(firstMarketPosition.x - secondMarketPosition.x, 2) + Math.pow(firstMarketPosition.y - secondMarketPosition.y, 2)); 37 25 38 26 // If markets belong to different players, add gain from international trading 39 27 var ownerFirstMarket = Engine.QueryInterface(firstMarket, IID_Ownership).GetOwner(); 40 28 var ownerSecondMarket = Engine.QueryInterface(secondMarket, IID_Ownership).GetOwner(); 41 if (ownerFirstMarket != ownerSecondMarket) 42 { 43 gain.market1Gain = gain.traderGain * ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_ADDITION, firstMarket) / 100; 44 gain.market1Owner = ownerFirstMarket; 45 gain.market2Gain = gain.traderGain * ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_ADDITION, secondMarket) / 100; 46 gain.market2Owner = ownerSecondMarket; 47 } 29 30 return CalculateMarketConnectionBonusDirect(distance, tradeOwner, ownerFirstMarket != ownerSecondMarket); 31 } 48 32 49 // Add potential trade multipliers and roundings 50 var cmpPlayer = trader ? QueryOwnerInterface(trader) : QueryOwnerInterface(firstMarket); 33 function CalculateMarketConnectionBonusDirect(distance, tradeOwner, international) 34 { 35 var bonus = Math.log(distance*DISTANCE_FACTOR); 36 var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 37 var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(tradeOwner), IID_Player); 51 38 if (cmpPlayer) 52 gain.traderGain *= cmpPlayer.GetTradeRateMultiplier(); 53 gain.traderGain = Math.round(gain.traderGain); 39 bonus *= cmpPlayer.GetTradeRateMultiplier(); 54 40 55 if (ownerFirstMarket != ownerSecondMarket) 56 { 57 if ((cmpPlayer = QueryOwnerInterface(firstMarket))) 58 gain.market1Gain *= cmpPlayer.GetTradeRateMultiplier(); 59 gain.market1Gain = Math.round(gain.market1Gain); 60 61 if ((cmpPlayer = QueryOwnerInterface(secondMarket))) 62 gain.market2Gain *= cmpPlayer.GetTradeRateMultiplier(); 63 gain.market2Gain = Math.round(gain.market2Gain); 64 } 65 66 return gain; 41 if (international) 42 bonus *= +ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_MULTIPLICATION, tradeOwner); 43 return Math.max(0, bonus); 67 44 } 68 45 69 Engine.RegisterGlobal("CalculateTraderGain", CalculateTraderGain); 46 Engine.RegisterGlobal("CalculateMarketConnectionBonusDirect", CalculateMarketConnectionBonusDirect); 47 Engine.RegisterGlobal("CalculateMarketConnectionBonus", CalculateMarketConnectionBonus); -
binaries/data/mods/public/simulation/templates/template_formation.xml
40 40 <WalkSpeed>1.0</WalkSpeed> 41 41 <PassabilityClass>large</PassabilityClass> 42 42 </UnitMotion> 43 <Trader>44 <GainMultiplier>1.0</GainMultiplier>45 </Trader>46 43 </Entity> -
binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml
71 71 armor_ship_hullsheathing 72 72 </Technologies> 73 73 </ProductionQueue> 74 <TradeIncome> 75 <Efficiency>0.5</Efficiency> 76 </TradeIncome> 74 77 <Vision> 75 78 <Range>40</Range> 76 79 </Vision> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
45 45 <HeightOffset>6.0</HeightOffset> 46 46 </StatusBars> 47 47 <Trader> 48 < GainMultiplier>1.0</GainMultiplier>48 <ConnectionStrength>100</ConnectionStrength> 49 49 </Trader> 50 50 <UnitAI> 51 51 <DefaultStance>passive</DefaultStance> -
binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
2 2 <Entity parent="template_structure_economic"> 3 3 <BuildRestrictions> 4 4 <Category>Market</Category> 5 <Distance> 6 <FromClass>CivilCentre</FromClass> 7 <MaxDistance>140</MaxDistance> 8 </Distance> 5 9 </BuildRestrictions> 6 10 <Cost> 7 11 <BuildTime>150</BuildTime> … … 13 17 <Square width="33.0" depth="29.0"/> 14 18 <Height>8.0</Height> 15 19 </Footprint> 20 <GarrisonHolder> 21 <Max>10</Max> 22 <EjectHealth>0.2</EjectHealth> 23 <EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy> 24 <List datatype="tokens">Worker</List> 25 <BuffHeal>0</BuffHeal> 26 <LoadingRange>2</LoadingRange> 27 </GarrisonHolder> 16 28 <Health> 17 29 <Max>1500</Max> 18 30 <SpawnEntityOnDeath>rubble/rubble_stone_5x5</SpawnEntityOnDeath> … … 47 59 <Radius>40</Radius> 48 60 <Weight>30000</Weight> 49 61 </TerritoryInfluence> 62 <TradeIncome> 63 <Efficiency>1.0</Efficiency> 64 <GarrisonEfficiency> 65 <EmptyEfficiency>0.2</EmptyEfficiency> 66 </GarrisonEfficiency> 67 </TradeIncome> 50 68 <ProductionQueue> 51 69 <BatchTimeModifier>0.7</BatchTimeModifier> 52 70 <Technologies datatype="tokens"> -
binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml
18 18 <Tooltip>Trade resources between your own markets and those of your allies.</Tooltip> 19 19 <Formations disable=""/> 20 20 </Identity> 21 <Loot> 22 <xp>10</xp> 23 <food>25</food> 24 <wood>25</wood> 25 <stone>15</stone> 26 <metal>15</metal> 27 </Loot> 21 28 <Sound> 22 29 <SoundGroups> 23 30 <select>voice/{lang}/civ/civ_{gender}_select.xml</select> … … 39 46 </SoundGroups> 40 47 </Sound> 41 48 <Trader> 42 < GainMultiplier>1.0</GainMultiplier>49 <ConnectionStrength>50</ConnectionStrength> 43 50 </Trader> 44 51 <UnitAI> 45 52 <CanGuard>false</CanGuard> -
binaries/data/mods/public/simulation/templates/special/player.xml
30 30 <Library>1</Library> 31 31 <Lighthouse>1</Lighthouse> 32 32 <Juggernaut>1</Juggernaut> 33 <Market>0</Market> 33 34 </Limits> 34 35 <LimitChangers> 36 <Market> 37 <CivilCentre>1</CivilCentre> 38 </Market> 35 39 <WarDog> 36 40 <Kennel>10</Kennel> 37 41 </WarDog> -
binaries/data/mods/public/simulation/templates/structures/cart_dock.xml
27 27 -training_naval_architects 28 28 </Technologies> 29 29 </ProductionQueue> 30 <TradeIncome> 31 <Efficiency>0.8</Efficiency> 32 </TradeIncome> 30 33 <VisualActor> 31 34 <Actor>structures/carthaginians/dock.xml</Actor> 32 35 <FoundationActor>structures/fndn_6x4_dock.xml</FoundationActor> -
binaries/data/mods/public/simulation/templates/structures/pers_market.xml
15 15 <Obstruction> 16 16 <Static width="24.0" depth="24.0"/> 17 17 </Obstruction> 18 <TradeIncome> 19 <Efficiency>1.25</Efficiency> 20 </TradeIncome> 18 21 <VisualActor> 19 22 <Actor>structures/persians/market.xml</Actor> 20 23 <FoundationActor>structures/fndn_5x5.xml</FoundationActor> -
binaries/data/mods/public/simulation/templates/units/cart_ship_merchant.xml
12 12 <Icon>units/cart_ship_merchant.png</Icon> 13 13 <RequiredTechnology>phase_village</RequiredTechnology> 14 14 </Identity> 15 <Trader>16 <GainMultiplier>1.25</GainMultiplier>17 </Trader>18 15 <VisualActor> 19 16 <Actor>structures/carthaginians/merchant_ship.xml</Actor> 20 17 </VisualActor> -
binaries/data/mods/public/simulation/templates/units/pers_support_trader.xml
20 20 <death>actor/fauna/death/death_camel.xml</death> 21 21 </SoundGroups> 22 22 </Sound> 23 <Trader>24 <GainMultiplier>1.25</GainMultiplier>25 </Trader>26 23 <VisualActor> 27 24 <Actor>units/persians/trader.xml</Actor> 28 25 </VisualActor>