1
1
package com.unciv.models.ruleset.unique
2
2
3
3
import com.unciv.Constants
4
- import com.unciv.logic.IsPartOfGameInfoSerialization
5
4
import com.unciv.logic.city.City
6
5
import com.unciv.logic.civilization.Civilization
7
6
import com.unciv.models.ruleset.GlobalUniques
@@ -12,7 +11,6 @@ import com.unciv.models.translations.getModifiers
12
11
import com.unciv.models.translations.getPlaceholderParameters
13
12
import com.unciv.models.translations.getPlaceholderText
14
13
import com.unciv.models.translations.removeConditionals
15
- import java.util.EnumMap
16
14
import kotlin.math.max
17
15
18
16
@@ -219,197 +217,3 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
219
217
fun getDisplayText (): String = if (modifiers.none { it.isHiddenToUsers() }) text
220
218
else text.removeConditionals() + " " + modifiers.filter { ! it.isHiddenToUsers() }.joinToString(" " ) { " <${it.text} >" }
221
219
}
222
-
223
- /* * Used to cache results of getMatchingUniques
224
- * Must only be used when we're sure the matching uniques will not change in the meantime */
225
- class LocalUniqueCache (val cache : Boolean = true ) {
226
- // This stores sequences *that iterate directly on a list* - that is, pre-resolved
227
- private val keyToUniques = HashMap <String , Sequence <Unique >>()
228
-
229
- fun forCityGetMatchingUniques (
230
- city : City ,
231
- uniqueType : UniqueType ,
232
- stateForConditionals : StateForConditionals = city.state
233
- ): Sequence <Unique > {
234
- // City uniques are a combination of *global civ* uniques plus *city relevant* uniques (see City.getMatchingUniques())
235
- // We can cache the civ uniques separately, so if we have several cities using the same cache,
236
- // we can cache the list of *civ uniques* to reuse between cities.
237
-
238
- val citySpecificUniques = get(
239
- " city-${city.id} -${uniqueType.name} " ,
240
- city.getLocalMatchingUniques(uniqueType, StateForConditionals .IgnoreMultiplicationForCaching )
241
- ).filter { it.conditionalsApply(stateForConditionals) }
242
- .flatMap { it.getMultiplied(stateForConditionals) }
243
-
244
- val civUniques = forCivGetMatchingUniques(city.civ, uniqueType, stateForConditionals)
245
-
246
- return citySpecificUniques + civUniques
247
- }
248
-
249
- fun forCivGetMatchingUniques (
250
- civ : Civilization ,
251
- uniqueType : UniqueType ,
252
- stateForConditionals : StateForConditionals = civ.state
253
- ): Sequence <Unique > {
254
- val sequence = civ.getMatchingUniques(uniqueType, StateForConditionals .IgnoreMultiplicationForCaching )
255
- // The uniques CACHED are ALL civ uniques, regardless of conditional matching.
256
- // The uniques RETURNED are uniques AFTER conditional matching.
257
- // This allows reuse of the cached values, between runs with different conditionals -
258
- // for example, iterate on all tiles and get StatPercentForObject uniques relevant for each tile,
259
- // each tile will have different conditional state, but they will all reuse the same list of uniques for the civ
260
- return get(
261
- " civ-${civ.civName} -${uniqueType.name} " ,
262
- sequence
263
- ).filter { it.conditionalsApply(stateForConditionals) }
264
- .flatMap { it.getMultiplied(stateForConditionals) }
265
- }
266
-
267
- /* * Get cached results as a sequence */
268
- private fun get (key : String , sequence : Sequence <Unique >): Sequence <Unique > {
269
- if (! cache) return sequence
270
- val valueInMap = keyToUniques[key]
271
- if (valueInMap != null ) return valueInMap
272
- // Iterate the sequence, save actual results as a list, as return a sequence to that
273
- val results = sequence.toList().asSequence()
274
- keyToUniques[key] = results
275
- return results
276
- }
277
- }
278
-
279
- open class UniqueMap () {
280
- protected val innerUniqueMap = HashMap <String , ArrayList <Unique >>()
281
-
282
- // *shares* the list of uniques with the other map, to save on memory and allocations
283
- // This is a memory/speed tradeoff, since there are *600 unique types*,
284
- // 750 including deprecated, and EnumMap creates a N-sized array where N is the number of objects in the enum
285
- val typedUniqueMap = EnumMap <UniqueType , ArrayList <Unique >>(UniqueType ::class .java)
286
-
287
- constructor (uniques: Sequence <Unique >) : this () {
288
- addUniques(uniques.asIterable())
289
- }
290
-
291
- fun isEmpty (): Boolean = innerUniqueMap.isEmpty()
292
-
293
- /* * Adds one [unique] unless it has a ConditionalTimedUnique conditional */
294
- open fun addUnique (unique : Unique ) {
295
- val existingArrayList = innerUniqueMap[unique.placeholderText]
296
- if (existingArrayList != null ) existingArrayList.add(unique)
297
- else innerUniqueMap[unique.placeholderText] = arrayListOf (unique)
298
-
299
- if (unique.type == null ) return
300
- if (typedUniqueMap[unique.type] != null ) return
301
- typedUniqueMap[unique.type] = innerUniqueMap[unique.placeholderText]
302
- }
303
-
304
- /* * Calls [addUnique] on each item from [uniques] */
305
- fun addUniques (uniques : Iterable <Unique >) {
306
- for (unique in uniques) addUnique(unique)
307
- }
308
-
309
- fun removeUnique (unique : Unique ) {
310
- val existingArrayList = innerUniqueMap[unique.placeholderText]
311
- existingArrayList?.remove(unique)
312
- }
313
-
314
- fun clear () {
315
- innerUniqueMap.clear()
316
- typedUniqueMap.clear()
317
- }
318
-
319
- // Pure functions
320
-
321
- fun hasUnique (uniqueType : UniqueType , state : StateForConditionals = StateForConditionals .EmptyState ) =
322
- getUniques(uniqueType).any { it.conditionalsApply(state) && ! it.isTimedTriggerable }
323
-
324
- fun hasUnique (uniqueTag : String , state : StateForConditionals = StateForConditionals .EmptyState ) =
325
- getUniques(uniqueTag).any { it.conditionalsApply(state) && ! it.isTimedTriggerable }
326
-
327
- fun hasTagUnique (tagUnique : String ) =
328
- innerUniqueMap.containsKey(tagUnique)
329
-
330
- // 160ms vs 1000-1250ms/30s
331
- fun getUniques (uniqueType : UniqueType ) = typedUniqueMap[uniqueType]
332
- ?.asSequence()
333
- ? : emptySequence()
334
-
335
- fun getUniques (uniqueTag : String ) = innerUniqueMap[uniqueTag]
336
- ?.asSequence()
337
- ? : emptySequence()
338
-
339
- fun getMatchingUniques (uniqueType : UniqueType , state : StateForConditionals = StateForConditionals .EmptyState ) =
340
- getUniques(uniqueType)
341
- // Same as .filter | .flatMap, but more cpu/mem performant (7.7 GB vs ?? for test)
342
- .flatMap {
343
- when {
344
- it.isTimedTriggerable -> emptySequence()
345
- ! it.conditionalsApply(state) -> emptySequence()
346
- else -> it.getMultiplied(state)
347
- }
348
- }
349
-
350
- fun getMatchingUniques (uniqueTag : String , state : StateForConditionals = StateForConditionals .EmptyState ) =
351
- getUniques(uniqueTag)
352
- // Same as .filter | .flatMap, but more cpu/mem performant (7.7 GB vs ?? for test)
353
- .flatMap {
354
- when {
355
- it.isTimedTriggerable -> emptySequence()
356
- ! it.conditionalsApply(state) -> emptySequence()
357
- else -> it.getMultiplied(state)
358
- }
359
- }
360
-
361
- fun hasMatchingUnique (uniqueType : UniqueType , state : StateForConditionals = StateForConditionals .EmptyState ) =
362
- getUniques(uniqueType).any { it.conditionalsApply(state) }
363
-
364
- fun hasMatchingUnique (uniqueTag : String , state : StateForConditionals = StateForConditionals .EmptyState ) =
365
- getUniques(uniqueTag)
366
- .any { it.conditionalsApply(state) }
367
-
368
- fun getAllUniques () = innerUniqueMap.values.asSequence().flatten()
369
-
370
- fun getTriggeredUniques (trigger : UniqueType , stateForConditionals : StateForConditionals ,
371
- triggerFilter : (Unique ) -> Boolean = { true }): Sequence <Unique > {
372
- return getAllUniques().filter { unique ->
373
- unique.getModifiers(trigger).any(triggerFilter) && unique.conditionalsApply(stateForConditionals)
374
- }.flatMap { it.getMultiplied(stateForConditionals) }
375
- }
376
-
377
- companion object {
378
- val EMPTY = UniqueMap ()
379
- }
380
- }
381
-
382
- class TemporaryUnique () : IsPartOfGameInfoSerialization {
383
-
384
- constructor (uniqueObject: Unique , turns: Int ) : this () {
385
- val turnsText = uniqueObject.getModifiers(UniqueType .ConditionalTimedUnique ).first().text
386
- unique = uniqueObject.text.replaceFirst(" <$turnsText >" , " " ).trim()
387
- sourceObjectType = uniqueObject.sourceObjectType
388
- sourceObjectName = uniqueObject.sourceObjectName
389
- turnsLeft = turns
390
- }
391
-
392
- var unique: String = " "
393
-
394
- private var sourceObjectType: UniqueTarget ? = null
395
- private var sourceObjectName: String? = null
396
-
397
- @delegate:Transient
398
- val uniqueObject: Unique by lazy { Unique (unique, sourceObjectType, sourceObjectName) }
399
-
400
- var turnsLeft: Int = 0
401
- }
402
-
403
- fun ArrayList<TemporaryUnique>.endTurn () {
404
- for (unique in this ) {
405
- if (unique.turnsLeft >= 0 )
406
- unique.turnsLeft - = 1
407
- }
408
- removeAll { it.turnsLeft == 0 }
409
- }
410
-
411
- fun ArrayList<TemporaryUnique>.getMatchingUniques (uniqueType : UniqueType , stateForConditionals : StateForConditionals ): Sequence <Unique > {
412
- return this .asSequence()
413
- .map { it.uniqueObject }
414
- .filter { it.type == uniqueType && it.conditionalsApply(stateForConditionals) }
415
- }
0 commit comments