MNAI-U: unofficial build & bugfixes

I think it was with a mnai-2.7.2u based version that I first noticed the leaders getting extra wrong traits, but am not certain. I assume thegreekweegee was using the mnai-2.8-beta1u based version.
I'll wait for future reports then.

I don't know much about art, so I cannot help you with the golshan issue.

I don't see why it would be harder to read a file that had none of the default tags included for any unit except for one at the top or bottom just to make things easy to find without leaving the document. Making tag order irrelevant might be nice, but sometimes you want to be able to look at all the tags at a glance in case you forgot what tags are available.
I was replying to Calavente, who seemed to argue for not removing any default tags. For your solution my criticism would only apply to the top/bottom entry. I do not see that much of a difference between your and my solution, I think mine is a bit cleaner. Also, many text editors do not like to open a file twice, so with your solution you'd have to switch between the top and some other position in the file, as opposed to just switching between two files. The advantage of your solution is that new modders just have to find that one file, but that's what READMEs are for (or maybe I'll just refer to the example file in the header comment).
By the way, if you only need to know the order or availability of tags, you can always just look into the Schema file. For me, the only reason to add an example entry would be to allow people to copy/paste stuff, thus making editing a bit faster.

When I was eliminating extraneous tags from CIV4UnitInfos.xml before my last release I accidentally got rid of all the <iAdvancedStartCost> tags, which meant no unit could be placed in Advanced Start games. I fixed this an hour after the release, but it seems to me that it would make more sense to get rid of these tags again and have you assign <iAdvancedStartCost> the default value of 100.
That sounds reasonable, I will do that.
 
I understand that having all the default tags makes it easier to insert new effects, but it makes it so much harder to read the XML. It is impossible to see at a glance what a unit is doing, because there are so many <b.....>0</b...> tags. You usually cannot even see what entry you're currently in. And when you maintain a mod (and generally, a program), you do much more reading than writing.
That said, I guess the real problem is that the Tag order mostly makes no sense whatsoever. I'm thinking of just making the tag order optional - the DLL already supports this, as far as I can tell, but I'd have to look into how to configure the schema accordingly (as a fun fact the schema already allows you to place every tag that is defined in it in every other tag where you don't specifically declare model=closed. XDR really was a stupid schema format).
In fact I was arguing to keep a model in the file, at top or bottom, for new modders, but also for an easy access to references. It would also help limit errors : this tag works for the units but not the promotions / or for the buildings and not the spells ...etc.
A readme would be more complicated IMO, but I see your point of 2 files. but then nothing blocks having both a full set IN the file and a full set in a readme.
However the best improvement would be what you propose: remove the order ! (that one is really a ***** !)(but a full list somewhere would still be useful / needed)

Are there "easy" ways to transfer tags from promotions/units/buildings to another file ? (or only dll ?)
I remember that there are some effects that only work when applied through promotions and others units effects not applicable through promotions.
 
As far as the default tag thing goes I think that removing tag entries that aren't needed/used in each unit's entry itself would make the files much easier to read but also having an example entry that lists all the tags in the correct order somewhere as well whether in the same file or a different one as long as it's easy to find. The easy to find part is key here especially new modders. I once assumed the schema served that purpose but I then noticed it didn't always line up with what was going in the xml files and things sometimes repeat themselves in it so I never really did figure out for sure what goes in what order or what the schema actually controls and always just winged it. Also I had to lol at you guys using professional python editors and me over here still using notepad++ only barely knowing what I'm doing in python to begin with, at least I give good bug reports!
 
Are there "easy" ways to transfer tags from promotions/units/buildings to another file ? (or only dll ?)
You'd need DLL work for that.

I once assumed the schema served that purpose but I then noticed it didn't always line up with what was going in the xml files and things sometimes repeat themselves in it so I never really did figure out for sure what goes in what order or what the schema actually controls and always just winged it.
Tag order is only controlled by the schema, the DLL does not care (or at least I never saw any code that suggested otherwise). Some Schema files missed declaring some tags properly, but I think I fixed that a few weeks ago.
 
If you could easily transfer tags from units to promotions or vice versa, I would have long ago been using <PyPerTurn> effects for units and <PythonPostCombatWon> and <PythonPostCombatLost tags> for promotions.

Instead of the PythonPostCombat promotions I've been forced to use def onCombatResult(self, argsList): in CvEventManager.py

What I'd really like though is a python call I could use when units withdraw from combat, instead of only when combat results in the death of one unit or another.

In the short term though I think a higher priority would be a python function to check to see whether a unit is immune to a promotion, and changing the xml spells so that it won't think ineligible units are valid targets for promotion granting spells.
---


Could you let promotions grant the ability to move through rival territory without open borders or right of passage agreements? I believe Tholal already let them allow units to move through limited borders, but ignoring open borders entirely would be more useful.

I am mostly interested in letting Hidden or Invisible units ignore borders until they get caught, but then getting ejected or trapped if someone casts Revelation on them. If you prefer to make the <bInvisible> tag let units ignore borders I'd be fine with that. It might even be nicer if those units freedom of movement only applied when not within the line of sight of a unit able to see them while invisible. However, modders might also find a use for a separate tag that let units move through rival territory while fully visible.


--

I just fixed the setLevel issues as you recommended. Besides what you called out everything setting a unit's level took an unmodified level from another unit, so there should not be any way for it to be less than 1.

---

While looking through the python files I noticed my CvRandomEventInterface.py still includes a lot of code from vanilla BtS events that I don't think make any sense with FfH2. Some of them reference things from the XML which are not found in any FfH2 modmods, like 'SPECIALBUILDING_CATHEDRAL', ,'SPECIALBUILDING_MONASTERY', 'UNITCLASS_SPEARMAN', 'CIVIC_EMANCIPATION', etc.

Those are found in your version of CvRandomEventInterface.py too.

If you are cleaning up files to remove extra tags, to reduce file size and make them easier to read, you should probably clean up such events too.

---
I'm still getting these asserts:
Spoiler :
Code:
Assert Failed

File:  CvGame.cpp
Line:  5803
Expression:  getBuildingClassCreatedCount(eIndex) <= GC.getBuildingClassInfo(eIndex).getMaxGlobalInstances()
Message:  Index is expected to be within maximum bounds (invalid Index)

----------------------------------------------------------
Assert FailedAssert Failed

File:  CyGlobalContext.cpp
Line:  70
Expression:  idx>=0
Message:

----------------------------------------------------------


File:  CvCity.cpp
Line:  8326
Expression:  getBonusGoodHappiness() >= 0
Message:

----------------------------------------------------------
Assert Failed
Assert Failed

File:  CvInfos.cpp
Line:  2673
Expression:  i > -1
Message:  Index out of bounds

----------------------------------------------------------

File:  CvCity.cpp
Line:  8338
Expression:  getBonusBadHappiness() <= 0
Message:

----------------------------------------------------------
Assert Failed

File:  CvCity.cpp
Line:  7751
Expression:  getBonusGoodHealth() >= 0
Message:

----------------------------------------------------------
Assert Failed

File:  CvCity.cpp
Line:  7753
Expression:  getBonusGoodHealth() >= 0
Message:  getBonusGoodHealth is expected to be >= 0

----------------------------------------------------------
Assert Failed

File:  CvArtFileMgr.cpp
Line:  182
Expression:  false
Message:  get##name##ArtInfo:  was not found

----------------------------------------------------------
Assert Failed

File:  CvUnitAI.cpp
Line:  25485
Expression:  false
Message:

----------------------------------------------------------
Assert Failed

File:  CvPlot.cpp
Line:  8638
Expression:  getStolenVisibilityCount(eTeam) >= 0
Message:

----------------------------------------------------------
Assert Failed

File:  z:\repos\mnai_unofficial\cvgamecoredll\CvPlayerAI.h
Line:  25
Expression:  ePlayer != NO_PLAYER
Message:  Player is not assigned a valid value

----------------------------------------------------------
Assert Failed

File:  CvInitCore.cpp
Line:  1621
Expression:  eID < ((50) + 1)
Message:  Index in CvInitCore::getCiv expected to be < 51

----------------------------------------------------------
 
Last edited:
I get them repeatedly as I play through the scenario. The later asserts (those outside the first 4) come into play when I fail to answer the riddle correctly and the Meshabber of Dis kills me.
Did you get all these asserts in MNAI-U or in MagisterModMod? I played through the scenario up to the Meshabber and didn't get any.

@MagisterCultuum: You're suggestions seem reasonable. I will look into these and your earlier suggestions before the next release (and probably implement them if it isn't unreasonably complicated).
 
Did you get all these asserts in MNAI-U or in MagisterModMod? I played through the scenario up to the Meshabber and didn't get any.

MagisterModMod. I don't play MNAI-U. You tried playing through the scenario with MNAI-U?
 
I just got a little alloyed that there was not a way to toggle pUnit.setIgnoreHide(bool) in worldbuilder, so I added that function to C:\Program Files (x86)\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Magister Modmod for FfH2\Assets\python\Screens\PlatyBuilder\WBUnitScreen.py

I tried to similarly add the ability to toggle pUnit.setBlockading(bool) , but found that it does not work because pUnit.isBlockading(bool) is not exposed to python.

Would you mind exposing that?
 
In the short term though I think a higher priority would be a python function to check to see whether a unit is immune to a promotion, and changing the xml spells so that it won't think ineligible units are valid targets for promotion granting spells.
It seems the current situation with units affected by Promotion-granting spells is a bit weird. Currently, a spell adding promotion P to units only does that if the target unit does not have P and has a UnitCombat allowed by P (and also, P can never be applied to a unit immune to P). I'd like to change those requirements to the canAcquirePromotion() function to streamline things (for example, PromotionCombatApply uses canAcquirePromotion()). That would add the following requirements to add promotion P to unit U: U must have all PrereqPromotions of P, P must not be a race nor an equipment promotion, U must have the prerequisite level and religion of P, U must be alive if P requires it, the owner of U must have the proper Bonus, Tech and state religion required by P. (see also canAcquirePromotion() in the DLL source)
To me it doesn't seem like this would cause any problems, but I figured I'd ask you anyway because you have more experience with spells and do crazy stuff in MagisterModMod. Having it streamlined like this would allow for easier fixes for your problems.
An alternative would be to allow more or less every promotion to be applied and specify all requirements in the spells via my upcoming prereq system.
 
My first thought is that using canAcquirePromotion() would probably break all of the promotion-granting spells.

Wouldn't it mean that spells could not be used to add promotions which cannot be gained though experience?

I cannot off the top of my head think of any of the promotion granting spells that add promotions that normally can be gained through experience.

Most if not all of them add Effects, i.e., promotions with either a TECH_NEVER or <MinLevel>-1 prereq tag.

What use would the Courage or Shaddowwalk spells be if those were promotions units could normally gain without any arcane assistance?




Also, there are spells intended to add equipment promotions. Most are picking up equipment from another unit, building, etc. Most have the bBuffCasterOnly tag which also lets the spell ignore the promotion's unitcombats and the units magic immunity.

In my modmod however the Spellstaff promotion is a piece of equipment that Enchantment 3 can add to the caster and Enchantment Affinity + Channeling 3 can add to all arcane units in the stack.


In addition to reforming spells spells so you cannot cast them when being immune to a promotion would make them have no effect, I think we really need to expose a python function to tell us whether a unit is immune to a promotion. It is essential for many PyHelp strings to be accurate and would be quite useful elsewhere too.
 
The <MinLevel>-1</iMinLevel> check is done in canPromote(), which is used to check if a unit can purchase a promotion the normal way. TECH_NEVER is not used for promotions in MNAI (why would it, when you can do the same with MinLevel).

But the equipment promotions is a legitimate concern. I think I can move that check from canAcquirePromotion() to canPromote(), though.
Another thing I missed is that spells should be applied to units with no unitcombat, but these units cannot purchase any promotions.

I think we really need to expose a python function to tell us whether a unit is immune to a promotion
Exactly. I will probably expose CvUnit::isPromotionImmune(), but that will only tell you what the PromotionImmune XML tags say. That means, if isPromotionImmune() says false, it doesn't necessarily mean any given game mechanic will apply that promotion to the unit. For example, PromotionCombatApply will currently use canAcquirePromotion() and not apply any equipment promotion, or any promotion with a tech the target unit's owner doesn't have, including TECH_NEVER. Spells, on the other hand, will apply a promotion if the target unit is not immune and has the right unitcombat (or has no unitcombat). You'd have to check both these things in python.
What I want to do is to unify all that so you only ever have to check canAcquirePromotion() to see if a promotion will be applied by one of these mechanics. Without going into details, this would also simplify the C++ code necessary for disabling spells when they would have no effect.
 
I found another inconsistency: The spells with <bBuffCasterOnly>1</bBuffCasterOnly> apply promotions without checking for unitcombat (only for immunity). So I think the cleanest solution would actually be to only check for immunity when applying promotions to target units (and whether the promotion is already present, of course). For promotions that used to specify only specific unitcombats, the spell will need to specify that. I think this is actually a good thing, as the spell can then list these requirements itself, rather than people having to look up the promotion.
Some unitcombat requirements seem actually unintentional. Courage, for example, is not applied to beast units, but to animal units. Sounds like an oversight, maybe when the beast unitcombat were added?

Thoughts/complains? I'll need to add/adjust the new prereq system beforehand, so if you have comments on that, please state them in the next few days, ideally.
 
The main reason that this release is called "beta" is that the next release (2.8-beta2u or 2.8.0u) will probably again break savegames.
How soon might the next non-beta (i.e. post-savegame-breaking) version be out. I don't want to get too invested in my current game if I'm just going to break it soon. Doing my best to keep up with the latest versions, so I can try to helpfully playtest what y'all are doing. Thanks for doing it!
 
I just added adding a couple of new naval units (Merchantman and Smuggler Ship) in my modmod which can conduct trade missions like Great Merchants can (although not quite as profitable).

I don't like how conducting the trade mission kills not only the unit but also any cargo it is carrying though.

Could you make the trade mission unload any cargo before killing the unit, just like Tholal long ago made units unload their cargo just before their duration expires?

(By the way, do immortal units still die permanently when in a ship that is destroyed? I really wish they wouldn't.)


The Smuggler Ship I added has <bRivalTerritory>1</bRivalTerritory>, but I found that the ship still cannot enter a city to conduct a trade mission unless I have an Open Borders Agreement. (Not even a Right of Passage Agreement helps.) Could you change that in the next release too?
 
I don't like how conducting the trade mission kills not only the unit but also any cargo it is carrying though.

Could you make the trade mission unload any cargo before killing the unit, just like Tholal long ago made units unload their cargo just before their duration expires?
This will be fixed in the next version.

The Smuggler Ship I added has <bRivalTerritory>1</bRivalTerritory>, but I found that the ship still cannot enter a city to conduct a trade mission unless I have an Open Borders Agreement. (Not even a Right of Passage Agreement helps.) Could you change that in the next release too?
Caravel uses bRivalTerritory, and judging from the source code, it is intended that they can't enter cities. I'll have to investigate why. Could you post the whole XML code for smuggler ship? In particular, I would be interested whether it is invisible.
 
The thought just came to mind that it might be interesting to let merchant ships hurry production (like lesser Great Engineers, or Soldiers of Kilmorph) or add culture (like lesser Great Bards, or basic disciples), in which case I'd also want the Hurry and Great Works mission to also unload rather than kill their cargo. I cannot currently think of a scenario when I might want a unit with cargo to be sacrificed for a golden age or a technology, but you might as well make those missions unload cargo before killing the unit too if the code is simple enough.


If I had to guess, I'd say the reason Caravels were not allowed to enter cities without open borders was to stop them from dropping a unit like a Nightwatch there. Maybe it would be better to exclude ships that are currently carrying cargo, or which have cargo that is not also bRivalTerritory?


Right now the Smuggler xml defines is this
Spoiler :
Code:
       <UnitInfo>
           <Class>UNITCLASS_SMUGGLER</Class>
           <Type>UNIT_SMUGGLER</Type>
           <Combat>UNITCOMBAT_NAVAL</Combat>
           <Domain>DOMAIN_SEA</Domain>
           <DefaultUnitAI>UNITAI_MERCHANT</DefaultUnitAI>
           <Description>TXT_KEY_UNIT_SMUGGLER</Description>
           <Civilopedia>TXT_KEY_UNIT_PLACEHOLDER_PEDIA</Civilopedia>
           <Advisor>ADVISOR_ECONOMY</Advisor>
           <bMilitarySupport>0</bMilitarySupport>
           <bMilitaryProduction>0</bMilitaryProduction>
           <bPillage>0</bPillage>
           <bMechanized>1</bMechanized>
           <bRenderBelowWater>1</bRenderBelowWater>
           <bMilitaryTrade>1</bMilitaryTrade>
           <bOnlyDefensive>1</bOnlyDefensive>
           <bRivalTerritory>1</bRivalTerritory>
           <bSabotage>0</bSabotage>
           <bDestroy>1</bDestroy>
           <bStealPlans>1</bStealPlans>
           <bInvestigate>1</bInvestigate>
           <bInvisible>0</bInvisible>
           <UnitClassUpgrades>
               <UnitClassUpgrade>
                   <UnitClassUpgradeType>UNITCLASS_FIRE_SHIP</UnitClassUpgradeType>
                   <bUnitClassUpgrade>1</bUnitClassUpgrade>
               </UnitClassUpgrade>
           </UnitClassUpgrades>
           <UnitAIs>
               <UnitAI>
                   <UnitAIType>UNITAI_MERCHANT</UnitAIType>
                   <bUnitAI>1</bUnitAI>
               </UnitAI>
               <UnitAI>
                   <UnitAIType>UNITAI_EXPLORE_SEA</UnitAIType>
                   <bUnitAI>1</bUnitAI>
               </UnitAI>
               <UnitAI>
                   <UnitAIType>UNITAI_SETTLER_SEA</UnitAIType>
                   <bUnitAI>1</bUnitAI>
               </UnitAI>
           </UnitAIs>
           <Buildings>
               <Building>
                   <BuildingType>BUILDING_SMUGGLERS_PORT</BuildingType>
                   <bBuilding>1</bBuilding>
               </Building>
           </Buildings>
           <PrereqTech>TECH_TRADE</PrereqTech>
           <TechTypes>
               <PrereqTech>TECH_SAILING</PrereqTech>
               <PrereqTech>TECH_CURRENCY</PrereqTech>
           </TechTypes>
           <iCost>300</iCost>
           <iAdvancedStartCost>100</iAdvancedStartCost>
           <iMinAreaSize>20</iMinAreaSize>
           <iMoves>4</iMoves>
           <TerrainImpassables>
               <TerrainImpassable>
                   <TerrainType>TERRAIN_OCEAN</TerrainType>
                   <bTerrainImpassable>1</bTerrainImpassable>
               </TerrainImpassable>
           </TerrainImpassables>
           <TerrainPassableTechs>
               <TerrainPassableTech>
                   <TerrainType>TERRAIN_OCEAN</TerrainType>
                   <PassableTech>TECH_ASTRONOMY</PassableTech>
               </TerrainPassableTech>
           </TerrainPassableTechs>
           <iCombat>4</iCombat>
           <iBaseTrade>100</iBaseTrade>
           <iTradeMultiplier>200</iTradeMultiplier>
           <iXPValueAttack>8</iXPValueAttack>
           <iXPValueDefense>4</iXPValueDefense>
           <iBombardRate>0</iBombardRate>
           <iWithdrawalProb>40</iWithdrawalProb>
           <DomainCargo>DOMAIN_LAND</DomainCargo>
           <iCargo>0</iCargo>
           <iAsset>9</iAsset>
           <iPower>12</iPower>
           <UnitMeshGroups>
           <iGroupSize>1</iGroupSize>
           <fMaxSpeed>2.25</fMaxSpeed>
           <fPadTime>1</fPadTime>
           <iMeleeWaveSize>1</iMeleeWaveSize>
           <iRangedWaveSize>1</iRangedWaveSize>
               <UnitMeshGroup>
                   <iRequired>1</iRequired>
                   <EarlyArtDefineTag>ART_DEF_UNIT_NETHERLANDS_OOSTINDIEVAARDER</EarlyArtDefineTag>
               </UnitMeshGroup>
           </UnitMeshGroups>
           <FormationType>FORMATION_TYPE_DEFAULT</FormationType>
           <HotKey/>
           <FreePromotions>
               <FreePromotion>
                   <PromotionType>PROMOTION_STEALTH</PromotionType>
                   <bFreePromotion>1</bFreePromotion>
               </FreePromotion>
               <FreePromotion>
                   <PromotionType>PROMOTION_HIDDEN</PromotionType>
                   <bFreePromotion>1</bFreePromotion>
               </FreePromotion>
           </FreePromotions>
           <bNeverObsolete>1</bNeverObsolete>
           <iCombatDefense>4</iCombatDefense>
           <iTier>1</iTier>
           <bCanMoveLimitedBorders>1</bCanMoveLimitedBorders>
           <iWithdrawlProbDefensive>40</iWithdrawlProbDefensive>
           <PrereqBuildingClass>BUILDINGCLASS_SMUGGLERS_PORT</PrereqBuildingClass>
           <PythonPostCombatLost>postCombatLostMerchant(pCaster, pOpponent)</PythonPostCombatLost>
       </UnitInfo>

I'm sure it is not relevant, but since it is referenced in that define I'll show that postCombatLostMerchant is used to give whoever captures a merchant vessel a random amount of gold based on how much it could have gained from a trade mission.
Code:
def postCombatLostMerchant(pCaster, pOpponent):
   info = gc.getUnitInfo(pCaster.getUnitType())
   iPlayer = pCaster.getOwner()
   pPlayer = gc.getPlayer(iPlayer)
   iPlayerO = pOpponent.getOwner()
   pPlayerO = gc.getPlayer(iPlayerO)
   iTradeGoods = CyGame().getSorenRandNum(info.getBaseTrade(), "Merchant base" + str(pCaster.getName()) +" Raided by " + str(pOpponent.getName()))
   pCity = CyMap().findCity(pCaster.getX(), pCaster.getY(), iPlayer, TeamTypes.NO_TEAM, False, pCaster.getDomainType() == gc.getInfoTypeForString('DOMAIN_SEA'), TeamTypes.NO_TEAM, DirectionTypes.NO_DIRECTION, pPlayer.getCity(-1))
   pCityO = CyMap().findCity(pCaster.getX(), pCaster.getY(), iPlayerO, TeamTypes.NO_TEAM, False, pCaster.getDomainType() == gc.getInfoTypeForString('DOMAIN_SEA'), TeamTypes.NO_TEAM, DirectionTypes.NO_DIRECTION, pPlayerO.getCity(-1))
   if pCity.isNone():
       pCity = pPlayer.getCapitalCity()
   if pCityO.isNone():
       pCityO = pPlayerO.getCapitalCity()
   if not (pCity.isNone() or pCityO.isNone()):
       iProfit = pCity.calculateTradeProfit(pCityO) * info.getTradeMultiplier()/4
       if iProfit > 0:
           iTradeGoods += CyGame().getSorenRandNum(iProfit, "Merchant modifier" + str(pCaster.getName()) +" Raided by " + str(pOpponent.getName()))
   if iTradeGoods > 0:
       pPlayerO.changeGold(iTradeGoods)
       CyInterface().addMessage(iPlayerO, True, 25, CyTranslator().getText("TXT_KEY_MESSAGE_GOLD_FROM_COMBAT", (iTradeGoods,)), '', 1, info.getButton(), ColorTypes(8), pCaster.getX(), pCaster.getY(), True, True)


I originally used bInvisible, but when I noticed the unit was still not visible to units with Perfect Sight I changed it to Stealth+Hidden instead. Neither influenced whether the unit could enter cities.




By the way, I have noticed that invisible units (if they do not have Blitz or Hidden Nationality) that have already attacked in the current turn so cannot attack again (I generally see this with summoned Angels of Death) are able to enter tiles occupied by enemy units (including enemy cities), but that invisible units with <bOnlyDefensive>1</bOnlyDefensive> never can. That behavior strikes me as odd.

It would be much more interesting if invisible + only defensive units (like Agents of Esus) could enter enemy occupied tiles to use spells like Kidnap, Steal, Embezzle, Disrupt, etc or abilities like Destroy Production or Steal Plans.


p.s. If CvUnit::isBlockading() is exposed in the next release you might as well include this new WBUnitScreen.py (and a WorldBuilder_CIV4GameText.xml with one new tag it uses) so that Worldbuilder can be used to change whether a unit is blockading. I have attached the files.
 

Attachments

Last edited:
If I had to guess, I'd say the reason Caravels were not allowed to enter cities without open borders was to stop them from dropping a unit like a Nightwatch there.
Are you sure they can do this now? AFAIK, caravels cannot carry hidden nationality units, so couldn't carry a nightwatch unless it'd declared nationality. Maybe you're thinking of pirates. They can carry hidden nationality units.
 
Last edited:
Back
Top Bottom