Changeset 25206
- Timestamp:
- Apr 8, 2021, 7:31:34 AM (3 years ago)
- Location:
- ps/trunk/binaries/data/mods/public/simulation/components
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
ps/trunk/binaries/data/mods/public/simulation/components/ResourceGatherer.js
r25139 r25206 32 32 "</element>"; 33 33 34 35 36 37 38 39 34 40 ResourceGatherer.prototype.Init = function() 35 41 { … … 162 168 { 163 169 return { "max": +this.template.MaxDistance, "min": 0 }; 164 // maybe this should depend on the unit or target or something? 165 }; 166 167 /** 168 * Gather from the target entity. This should only be called after a successful range check, 169 * and if the target has a compatible ResourceSupply. 170 * Call interval will be determined by gather rate, so always gather 1 amount when called. 171 */ 172 ResourceGatherer.prototype.PerformGather = function(target) 173 { 174 if (!this.GetTargetGatherRate(target)) 175 return { "exhausted": true }; 176 177 let gatherAmount = 1; 170 }; 171 172 /** 173 * @param {number} target - The target to gather from. 174 * @param {number} callerIID - The IID to notify on specific events. 175 * @return {boolean} - Whether we started gathering. 176 */ 177 ResourceGatherer.prototype.StartGathering = function(target, callerIID) 178 { 179 if (this.target) 180 this.StopGathering(); 181 182 let rate = this.GetTargetGatherRate(target); 183 if (!rate) 184 return false; 178 185 179 186 let cmpResourceSupply = Engine.QueryInterface(target, IID_ResourceSupply); 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 180 275 let type = cmpResourceSupply.GetType(); 181 182 // Initialise the carried count if necessary183 276 if (!this.carrying[type.generic]) 184 277 this.carrying[type.generic] = 0; 185 278 186 // Find the maximum so we won't exceed our capacity187 279 let maxGathered = this.GetCapacity(type.generic) - this.carrying[type.generic]; 188 189 let status = cmpResourceSupply.TakeResources(Math.min(gatherAmount, maxGathered)); 190 280 let status = cmpResourceSupply.TakeResources(Math.min(this.GATHER_AMOUNT, maxGathered)); 191 281 this.carrying[type.generic] += status.amount; 192 193 282 this.lastCarriedType = type; 194 283 195 284 // Update stats of how much the player collected. 196 285 // (We have to do it here rather than at the dropsite, because we 197 // need to know what subtype it was )286 // need to know what subtype it was) 198 287 let cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 199 288 if (cmpStatisticsTracker) … … 202 291 Engine.PostMessage(this.entity, MT_ResourceCarryingChanged, { "to": this.GetCarryingStatus() }); 203 292 204 205 return { 206 "amount": status.amount, 207 "exhausted": status.exhausted, 208 "filled": this.carrying[type.generic] >= this.GetCapacity(type.generic) 209 }; 293 if (!this.CanCarryMore(type.generic)) 294 this.StopGathering("InventoryFilled"); 295 else if (status.exhausted) 296 this.StopGathering("TargetInvalidated"); 210 297 }; 211 298 … … 218 305 { 219 306 let cmpResourceSupply = QueryMiragedInterface(target, IID_ResourceSupply); 220 if (!cmpResourceSupply )307 if (!cmpResourceSupply) 221 308 return 0; 222 309 … … 225 312 let rate = 0; 226 313 if (type.specific) 227 rate = this.GetGatherRate(type.generic +"."+type.specific);314 rate = this.GetGatherRate(type.generictype.specific); 228 315 if (rate == 0 && type.generic) 229 316 rate = this.GetGatherRate(type.generic); … … 234 321 235 322 return rate; 323 324 325 326 327 328 329 330 331 236 332 }; 237 333 … … 377 473 }; 378 474 475 476 477 478 479 480 481 482 483 484 379 485 // Since we cache gather rates, we need to make sure we update them when tech changes. 380 486 // and when our owner change because owners can had different techs. -
ps/trunk/binaries/data/mods/public/simulation/components/TreasureCollecter.js
r24989 r25206 47 47 return false; 48 48 49 50 51 52 49 53 this.target = target; 50 54 this.callerIID = callerIID; … … 70 74 delete this.target; 71 75 72 // The callerIID component may start gathering again, 73 // replacing the callerIID, which gets deleted after 74 // the callerIID has finished. Hence save the data. 76 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); 77 if (cmpVisual) 78 cmpVisual.SelectAnimation("idle", false, 1.0); 79 80 // The callerIID component may start collecting again, 81 // replacing the callerIID, hence save that. 75 82 let callerIID = this.callerIID; 76 83 delete this.callerIID; -
ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js
r25192 r25206 2496 2496 "GATHERING": { 2497 2497 "enter": function() { 2498 this.gatheringTarget = this.order.data.target || INVALID_ENTITY; // deleted in "leave". 2499 2500 // Check if the resource is full. 2501 // Will only be added if we're not already in. 2502 let cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); 2503 if (!cmpSupply || !cmpSupply.AddActiveGatherer(this.entity)) 2504 { 2505 this.SetNextState("FINDINGNEWTARGET"); 2498 let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); 2499 if (!cmpResourceGatherer) 2500 { 2501 this.FinishOrder(); 2502 return true; 2503 } 2504 2505 if (!this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer)) 2506 { 2507 this.ProcessMessage("OutOfRange"); 2506 2508 return true; 2507 2509 } … … 2512 2514 this.order.data.autoharvest = true; 2513 2515 2514 // Calculate timing based on gather rates 2515 // This allows the gather rate to control how often we gather, instead of how much. 2516 let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); 2517 let rate = cmpResourceGatherer.GetTargetGatherRate(this.gatheringTarget); 2518 2519 if (!rate) 2520 { 2521 // Try to find another target if the current one stopped existing 2522 if (!Engine.QueryInterface(this.gatheringTarget, IID_Identity)) 2523 { 2524 this.SetNextState("FINDINGNEWTARGET"); 2525 return true; 2526 } 2527 2528 // No rate, give up on gathering 2529 this.FinishOrder(); 2516 if (!cmpResourceGatherer.StartGathering(this.order.data.target, IID_UnitAI)) 2517 { 2518 this.ProcessMessage("TargetInvalidated"); 2530 2519 return true; 2531 2520 } 2532 2521 2533 // Scale timing interval based on rate, and start timer 2534 // The offset should be at least as long as the repeat time so we use the same value for both. 2535 let offset = 1000 / rate; 2536 this.StartTimer(offset, offset); 2537 2538 // We want to start the gather animation as soon as possible, 2539 // but only if we're actually at the target and it's still alive 2540 // (else it'll look like we're chopping empty air). 2541 // (If it's not alive, the Timer handler will deal with sending us 2542 // off to a different target.) 2543 if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer)) 2544 { 2545 this.SetDefaultAnimationVariant(); 2546 this.FaceTowardsTarget(this.order.data.target); 2547 this.SelectAnimation("gather_" + this.order.data.type.specific); 2548 cmpResourceGatherer.AddToPlayerCounter(this.order.data.type.generic); 2549 } 2522 this.FaceTowardsTarget(this.order.data.target); 2550 2523 return false; 2551 2524 }, 2552 2525 2553 2526 "leave": function() { 2554 this.StopTimer();2555 2556 // Don't use ownership because this is called after a conversion/resignation2557 // and the ownership would be invalid then.2558 let cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply);2559 if (cmpSupply)2560 cmpSupply.RemoveGatherer(this.entity);2561 2562 2527 let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); 2563 2528 if (cmpResourceGatherer) 2564 cmpResourceGatherer.RemoveFromPlayerCounter(); 2565 2566 delete this.gatheringTarget; 2567 2568 this.ResetAnimation(); 2569 }, 2570 2571 "Timer": function(msg) { 2572 let resourceTemplate = this.order.data.template; 2573 let resourceType = this.order.data.type; 2574 2575 // TODO: we are leaking information here - if the target died in FOW, we'll know it's dead 2576 // straight away. 2577 // Seems one would have to listen to ownership changed messages to make it work correctly 2578 // but that's likely prohibitively expansive performance wise. 2579 2580 let cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); 2581 // If we can't gather from the target, find a new one. 2582 if (!cmpSupply || !cmpSupply.IsAvailableTo(this.entity) || 2583 !this.CanGather(this.gatheringTarget)) 2584 { 2529 cmpResourceGatherer.StopGathering(); 2530 }, 2531 2532 "InventoryFilled": function(msg) { 2533 let nearestDropsite = this.FindNearestDropsite(this.order.data.type.generic); 2534 if (!nearestDropsite) 2535 this.FinishOrder(); 2536 2537 this.PushOrderFront("ReturnResource", { "target": nearestDropsite, "force": false }); 2538 }, 2539 2540 "OutOfRange": function(msg) { 2541 if (this.MoveToTargetRange(this.order.data.target, IID_ResourceGatherer)) 2542 this.SetNextState("APPROACHING"); 2543 // Our target is no longer visible - go to its last known position first 2544 // and then hopefully it will become visible. 2545 else if (!this.CheckTargetVisible(this.order.data.target) && this.order.data.lastPos) 2546 this.PushOrderFront("Walk", { 2547 "x": this.order.data.lastPos.x, 2548 "z": this.order.data.lastPos.z, 2549 "force": this.order.data.force 2550 }); 2551 else 2585 2552 this.SetNextState("FINDINGNEWTARGET"); 2586 return; 2587 } 2588 2589 if (!this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer)) 2590 { 2591 // Try to follow the target 2592 if (this.MoveToTargetRange(this.gatheringTarget, IID_ResourceGatherer)) 2593 this.SetNextState("APPROACHING"); 2594 // Our target is no longer visible - go to its last known position first 2595 // and then hopefully it will become visible. 2596 else if (!this.CheckTargetVisible(this.gatheringTarget) && this.order.data.lastPos) 2597 this.PushOrderFront("Walk", { 2598 "x": this.order.data.lastPos.x, 2599 "z": this.order.data.lastPos.z, 2600 "force": this.order.data.force 2601 }); 2602 else 2603 this.SetNextState("FINDINGNEWTARGET"); 2604 return; 2605 } 2606 2607 2608 let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); 2609 2610 // If we've already got some resources but they're the wrong type, 2611 // drop them first to ensure we're only ever carrying one type 2612 if (cmpResourceGatherer.IsCarryingAnythingExcept(resourceType.generic)) 2613 cmpResourceGatherer.DropResources(); 2614 2615 this.FaceTowardsTarget(this.order.data.target); 2616 2617 let status = cmpResourceGatherer.PerformGather(this.gatheringTarget); 2618 2619 if (status.filled) 2620 { 2621 let nearestDropsite = this.FindNearestDropsite(resourceType.generic); 2622 if (nearestDropsite) 2623 { 2624 // (Keep this Gather order on the stack so we'll 2625 // continue gathering after returning) 2626 // However mark our target as invalid if it's exhausted, so we don't waste time 2627 // trying to gather from it. 2628 if (status.exhausted) 2629 this.order.data.target = INVALID_ENTITY; 2630 this.PushOrderFront("ReturnResource", { "target": nearestDropsite, "force": false }); 2631 return; 2632 } 2633 2634 // Oh no, couldn't find any drop sites. Give up on gathering. 2635 this.FinishOrder(); 2636 return; 2637 } 2638 2639 if (status.exhausted) 2640 this.SetNextState("FINDINGNEWTARGET"); 2553 }, 2554 2555 "TargetInvalidated": function(msg) { 2556 this.SetNextState("FINDINGNEWTARGET"); 2641 2557 }, 2642 2558 }, … … 2973 2889 } 2974 2890 this.FaceTowardsTarget(this.order.data.target); 2975 this.SelectAnimation("collecting_treasure");2976 2891 return false; 2977 2892 }, … … 2981 2896 if (cmpTreasureCollecter) 2982 2897 cmpTreasureCollecter.StopCollecting(); 2983 this.ResetAnimation();2984 2898 }, 2985 2899 -
ps/trunk/binaries/data/mods/public/simulation/components/interfaces/ResourceGatherer.js
r18581 r25206 2 2 3 3 /** 4 * Message of the form { "to": [{ "type": string, "amount": number, "max": number }] }4 * Message of the form { "to": [{ "type": string, "amount": number, "max":number }] } 5 5 * sent from ResourceGatherer component whenever the amount of carried resources changes. 6 6 */ -
ps/trunk/binaries/data/mods/public/simulation/components/tests/test_ResourceGatherer.js
r24670 r25206 2 2 "BuildSchema": () => { 3 3 let schema = ""; 4 for (let res of ["food", "metal" ])4 for (let res of ["food", "metal"]) 5 5 { 6 6 for (let subtype in ["meat", "grain"]) … … 14 14 "subtypes": { 15 15 "meat": "meat", 16 "grain": "grain" 16 "grain": "grain", 17 "tree": "tree" 17 18 } 18 19 }; … … 25 26 Engine.LoadComponentScript("interfaces/ResourceSupply.js"); 26 27 Engine.LoadComponentScript("interfaces/StatisticsTracker.js"); 28 29 27 30 Engine.LoadComponentScript("ResourceGatherer.js"); 31 32 33 34 35 28 36 29 37 Engine.RegisterGlobal("ApplyValueModificationsToEntity", (prop, oVal, ent) => oVal); 30 31 const entity = 11; 32 const target = 12; 38 Engine.RegisterGlobal("QueryOwnerInterface", () => {}); 39 let cmpTimer; 40 41 const gathererID = 11; 42 const dropsiteID = 12; 43 const supplyID = 13; 33 44 34 45 let template = { … … 36 47 "BaseSpeed": "1", 37 48 "Rates": { 38 "food.grain": "1" 49 "food.grain": "1", 50 "wood.tree": "2" 39 51 }, 40 52 "Capacities": { 41 "food": "10" 53 "food": "10", 54 "wood": "20" 42 55 } 43 56 }; 44 57 45 let cmpResourceGatherer = ConstructComponent( entity, "ResourceGatherer", template);58 let cmpResourceGatherer = ConstructComponent(, "ResourceGatherer", template); 46 59 cmpResourceGatherer.RecalculateGatherRates(); 47 60 cmpResourceGatherer.RecalculateCapacities(); … … 57 70 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.GetCarryingStatus(), []); 58 71 59 // Test gathering.60 AddMock(target, IID_ResourceSupply, {61 "GetType": () => {62 return {63 "generic": "food",64 "specific": "grain"65 };66 },67 "TakeResources": (amount) => {68 return {69 "amount": amount,70 "exhausted": false71 };72 },73 "GetDiminishingReturns": () => null74 });75 76 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.PerformGather(target), {77 "amount": 1,78 "exhausted": false,79 "filled": false80 });81 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.GetCarryingStatus(), [{82 "type": "food",83 "amount": 1,84 "max": 1085 }]);86 87 72 // Test committing resources. 88 AddMock( target, IID_ResourceDropsite, {73 AddMock(, IID_ResourceDropsite, { 89 74 "ReceiveResources": (resources, ent) => { 90 75 return { … … 93 78 } 94 79 }); 95 96 cmpResourceGatherer.CommitResources(target); 80 cmpResourceGatherer.GiveResources([{ "type": "food", "amount": 1 }]); 81 82 cmpResourceGatherer.CommitResources(dropsiteID); 97 83 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.GetCarryingStatus(), []); 98 84 … … 104 90 "amount": 1 105 91 }]); 106 cmpResourceGatherer.CommitResources( target);92 cmpResourceGatherer.CommitResources(); 107 93 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.GetCarryingStatus(), [ { 108 94 "type": "wood", 109 95 "amount": 1, 110 "max": 096 "max": 0 111 97 }]); 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 -
ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Resources.js
r24992 r25206 39 39 Engine.LoadComponentScript("interfaces/ResourceSupply.js"); 40 40 Engine.LoadComponentScript("interfaces/StatisticsTracker.js"); 41 42 41 43 Engine.LoadComponentScript("Player.js"); 42 44 Engine.LoadComponentScript("ResourceDropsite.js"); 43 45 Engine.LoadComponentScript("ResourceGatherer.js"); 44 46 Engine.LoadComponentScript("ResourceSupply.js"); 47 45 48 46 49 Engine.RegisterGlobal("ApplyValueModificationsToEntity", (prop, oVal, ent) => oVal); 50 51 52 53 54 47 55 48 56 const owner = 1; … … 100 108 // Test gathering. 101 109 102 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.PerformGather(supply), { 103 "amount": 1, 104 "exhausted": false, 105 "filled": false 106 }); 110 TS_ASSERT(cmpResourceGatherer.StartGathering(supply)); 111 cmpTimer.OnUpdate({ "turnLength": 1 }); 107 112 TS_ASSERT_UNEVAL_EQUALS(cmpResourceSupply.GetCurrentAmount(), 999); 108 113 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.GetCarryingStatus(), [{ … … 111 116 "max": 10 112 117 }]); 118 113 119 114 120 // Test committing resources. … … 117 123 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.GetCarryingStatus(), []); 118 124 119 TS_ASSERT_UNEVAL_EQUALS(cmpResourceGatherer.PerformGather(supply), { 120 "amount": 1, 121 "exhausted": false, 122 "filled": false 123 }); 125 TS_ASSERT(cmpResourceGatherer.StartGathering(supply)); 126 cmpTimer.OnUpdate({ "turnLength": 1 }); 127 cmpResourceGatherer.StopGathering(); 124 128 cmpResourceGatherer.GiveResources([{ 125 129 "type": "wood",
Note:
See TracChangeset
for help on using the changeset viewer.