1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Boudicca's Rebellion - Creation Thread

Discussion in 'Civ2 - Scenario League' started by JPetroski, Jan 25, 2020.

  1. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    Double check that there is only one uLegionStandard in the object file.

    Try using
    civ.createUnit(object.uEvocatiFresh, object.tRomans,loser.location)

    This will tell us if the issue lies with civlua.createUnit, or something else. Actually, I think I just got it. The tile is currently occupied by the LegionStandard, which is owned by The Gods, and not the Romans. And, now, you're trying to create roman units on that tile before the Legion Standard has been deleted. civlua.createUnit mimics the old create unit event, which won't let you create units on tiles occupied by other tribes.
     
  2. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    Yes that solved it - I just used winner.location instead - good enough!
     
  3. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    At some point can you expand on these two please? I have a unit (Decianus Catus) that shouldn't be "killed" but should be "deleted", so I suppose that would be the example for the second one. That or I suppose this is where the munitions deletion would go?

    I can't really think of a situation where something would die outside of combat for the first one...

    Code:
    -- this happens whenever a unit 'dies', regardless of combat, as long as it is not replaced
    function unitKilledEvents.unitDeath(dyingUnit)
    end
    
    
    -- this happens if a unit is deleted (either through combat death, or by some other event,
    -- but not if the unit is disbanded)
    -- If the unit isn't being replaced, replacingUnit is nil
    function unitKilledEvents.unitDeleted(deletedUnit,replacingUnit)
    end
    
     
  4. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    Is this why this:

    Code:
    if loser.owner == object.tDruids and winner.owner == object.tRomans then 
        gen.justOnce("First Blood",limitedFunction)
        civ.ui.text(func.splitlines(object.xFuriesText))
        end 
    
    Is causing this:

    Code:
    File number 1 event ending at line 12 successfully parsed.  Event number is 1.
    ...est of Time\Scenario\Boudicca\LuaCore\generalLibrary.lua:2706: The Global table doesn't have a value associated with limitedFunction.
    stack traceback:
        [C]: in function 'error'
        ...est of Time\Scenario\Boudicca\LuaCore\generalLibrary.lua:2706: in metamethod '__index'
        ...vents\ContextTriggerEvents\MainScenario\onUnitKilled.lua:19: in function 'ContextTriggerEvents\MainScenario\onUnitKilled.unitKilledInCombat'
        ...ime\Scenario\Boudicca\LuaTriggerEvents\triggerEvents.lua:93: in function 'triggerEvents.unitKilledInCombat'
        D:\Test of Time\Scenario\Boudicca\events.lua:199: in upvalue 'doOnUnitDefeatedInCombat'
        D:\Test of Time\Scenario\Boudicca\events.lua:262: in function <D:\Test of Time\Scenario\Boudicca\events.lua:245>
    
     
  5. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    This is mostly so that if you have some behind the scenes 'maintenance' to do when a unit is deleted (maybe removing a unit from a table, or something) you can put it here. There are some functions in the general library to 'defeat', 'kill', and 'delete' units, and as part of them, they will run these functions.

    Suppose we had a mechanic where a general would 'join' a legion. That is, rather than having a separate "general" Suetonius unit, we have an entry in the state table saying that Suetonius is linked with a unit, say unit 212, which matters for some event. At some point, the legion gets 'demoted', and a new unit is created to replace it. Suetonius isn't "killed", so the state table entry needs to be transferred to the new unit. Or, maybe the unit is killed, and we don't want some other unit to get the 212 id number, and suddenly 'resurrect' Suetonius. This way, the end user doesn't have to 'look inside' the demotion module to deal with this, they simply have to change the unit deletion function, and use gen.deleteUnit any time they want to delete a unit in their own events.

    You could also have a situation where units might, say, die in the desert, and some event must happen when they die, even if there was no attacker. So, you'd put some event in the unit death.


    No, in the General Library, I have a list of functions at the top of the file for easier reference, just as comments. I forgot to put a comment for justOnce, but since it doesn't change anything in the code, I didn't want to distribute a new version for that "fix".

    Your code should be
    Code:
    if loser.owner == object.tDruids and winner.owner == object.tRomans then
    gen.justOnce("First Blood",function ()
    civ.ui.text(func.splitlines(object.xFuriesText))
    end)
    end
    Just as it would be using the justOnce function in OTR.
     
  6. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    I have a request for a particular function that I think many designers would find extremely handy. I believe you may already have the nuts and bolts for this done in OTR.

    When designing a single player scenario, one of the great challenges to overcome is having the AI sustain pressure. One way this has traditionally been done, is to have them gain new units after they kill certain "gatekeeper" units. In the base game, this meant that you had to know where the gatekeeper was (meaning it needed a unique unit), because you'd need to enter spawn points near it once it was killed. We can do one better here because we can use winner.location instead, which allows us to 1) have an infinite number of gatekeeper units sharing the same unitType, 2) not have to worry about picking the exact location ahead of time that units can spawn.

    What I'd appreciate is a function that allows winner.location + [number of tiles away] that basically will spawn units in a random unoccupied tile away. The tile should be definable (so not on ocean or impassable or whatever). This does a few things:

    1. Allows a truly broad front to develop;
    2. Prevents the human from seizing the initiative if they happen to have lost the unit while attacking, rather than if it was lost while defending.

    Spoiler :

    Right now, when Cerialis dies, bad things happen, but if he dies as an attacker, they can be quickly mitigated.

    Code:
        if loser.type == object.uCerialis then
        civ.ui.text("The rash Qunitus Petillius Cerialis, commander of the Legio IX Hispana, has fallen in battle, swept aside by the incredible and swelling forces of the Iceni.  Emboldened by their success, more tribesmen flock to the banner!")
        civlua.createUnit(object.uIceniSwordsmen, object.tIceni, {{winner.location.x,winner.location.y,winner.location.z}}, {count=8,randomize=false, veteran=false})
        civlua.createUnit(object.uIceniNobles, object.tIceni, {{winner.location.x,winner.location.y,winner.location.z}}, {count=4,randomize=false, veteran=false})
        civlua.createUnit(object.uIceniSpearmean, object.tIceni, {{winner.location.x,winner.location.y,winner.location.z}}, {count=12,randomize=false, veteran=false})
        civlua.createUnit(object.uIceniSkirm, object.tIceni, {{winner.location.x,winner.location.y,winner.location.z}}, {count=4,randomize=false, veteran=false})
        civlua.createUnit(object.uIceniArchers, object.tIceni, {{winner.location.x,winner.location.y,winner.location.z}}, {count=4,randomize=false, veteran=false})
        end
    
     
  7. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    Is this the sort of thing you were looking for?

    -- gen.getRandomNearbyUnoccupiedTile(tile,distance,allowedTiles) --> tile
    EDIT: Removed usage description, and General Library File, since the function was changed to behave differently.

    I think you mentioned at some point wanting a createUnit function similar to civlua.createUnit, but without some of its features (like not generating ground units on the ocean). Can you give me an idea of what you'd like for that?
     
    Last edited: Nov 28, 2020
    Dadais likes this.
  8. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    Yes - that's pretty much exactly it - will need to try and play around with it a bit but this should enhance playability/replay value.

    Basically, I'd like the functionality of the civlua command (mainly the randomization of a table of terrain squares for placement) without its drawbacks. The civ.CreateUnit command is weaker in that it does not randomize terrain squares. Anything that can't be randomized makes it easy to "cheat."

    Right now I'm trying to work through the events that absolutely need to be in Boudicca to get a playable game and I figure after that we'll just add on "decorations" that are put in place to show off the different modules/capabilities. I think I can handle most, but here are two that would go much faster if you simply built them than I bumble through it.

    1. A zone check system similar to Napoleon or OTR where the Romans need to a number of units in a certain area, lest an event triggers.

    We need to count the number of the following units:

    object.u1stCohortIIAugusta = civ.getUnitType(10)
    object.u1stCohortIXHispana = civ.getUnitType(11)
    object.u1stCohortXIVGMV = civ.getUnitType(12)
    object.u1stCohortXXVV = civ.getUnitType(13)
    object.uIIAugusta = civ.getUnitType(14)
    object.uIXHispana = civ.getUnitType(15)
    object.uXIVGMV = civ.getUnitType(16)
    object.uXXVV = civ.getUnitType(17)

    With each counting for one. The player can figure out which ones they can spare and which ones they can bring along but the point is they must have a certain number of these in particular places.

    Event 1: Romans must keep at least 3x legionary cohorts west of x44, y72 at all times or else each turn there is a good probability (call it 25%) that an event triggers where object.tSilures = civ.getTribe(2) declares war on the Romans and they get a bunch of units (I can fill in the createUnits)

    Event 2: Romans must keep at least 5x units north of y35 at all times or else there is a good (25%) chance that the object.tCaledonii = civ.getTribe(4) declare war on the Romans and they get a bunch of units (which again, I can handle)

    2. We need to figure out how to do the battle of Watling Street. Here's a fifteen minute clip if you have a few minutes at some point. The gist of it is that Seutonius gets to about London, realizes that he's screwed, starts retreating until he finds decent ground, and then held the battle. During the battle, both Roman flanks were protected by forests, and the Romans assembled in a narrow field between them. The Iceni's numbers were turned against them, the Romans adopted a wedge formation and cut them down, and then when the Iceni tried to retreat, they were trapped by their own baggage train.

    I was thinking we might have a situation where I put a few different Watling Street-esque terrain formations into the map, and then we could have a mechanism that works like this:

    Step 1: Romans must move east of x74 to trigger the event. A text box discussing that the uprising is much larger than anticipated.
    Step 2: the Romans retreat to ground of their choosing. They assemble their forces, select a tile, and press a button.
    Step 3: we teleport most of the Iceni army a few spaces away from the Romans, grouping it up near them, probably with the mechanism you came up with above.
    Step 4: we might also create a baggage train of a 3rd civ a few tiles behind the teleported Iceni. This might be tricky.
    Step 5: the Romans see if they can stackwipe a number of Iceni forces in one grand battle.

    I'm open to other ideas, just thought this would be one way to do it.
     
  9. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    Is what you want the civlua.createUnit command with the ability to override checking if the square can 'accept' the unit(s) created, or is there more to it than that?

    This reminded me of something that I meant to ask earlier. Based on some of the code you posted, you seem to be using one of the 'context' folders for your trigger events, instead of the UniversalTriggerEvents folder. Was this on purpose? If not, I think you can just copy the files directly to the UniversalTriggerEvents folder. The idea of context is that the events change, based on certain circumstances (such as, 'which tribe has the human player taken over'). If you don't need context events, you can just return 0 in the getContext() function. I think for testing, I set it to return 2, instead.

    What we can do, however, is choose the context randomly the first time a trigger happens, and store it in the state table. Any time after that, we use the context based on the one chosen the first time. Then, say, in Context 1, the tribes stay passive even if many troops are moved out, and in Context 2, they rebel. Maybe certain other events are more likely in one context or the other, so the player can make a guess as to whether he can safely move extra legions to face Boudicca.
     
  10. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    I've attached the code you need in countingCode.lua; I tested the code using the keyPressEvents.lua file, so I'd appreciate you post your copy, so I can replace mine.

    If you want to re-create the scotland and wales polygons (to see what they encompas), I've attached the polygon script (just use load script button to run it, after you've saved your changes).
     

    Attached Files:

  11. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    As an FYI (and I haven't messed with this yet) but here's a code I ran into trouble with.

    Code:
    File number 1 event ending at line 12 successfully parsed.  Event number is 1.
    ...e\Scenario\Boudicca\LuaRulesEvents\promotionSettings.lua:196: attempt to call a nil value (field 'overrideProducitonVetStatus')
    stack traceback:
        ...e\Scenario\Boudicca\LuaRulesEvents\promotionSettings.lua:196: in function 'promotionSettings.overrideProdVetStatus'
        D:\Test of Time\Scenario\Boudicca\events.lua:157: in function <D:\Test of Time\Scenario\Boudicca\events.lua:156>
    ...e\Scenario\Boudicca\LuaRulesEvents\promotionSettings.lua:196: attempt to call a nil value (field 'overrideProducitonVetStatus')
    stack traceback:
        ...e\Scenario\Boudicca\LuaRulesEvents\promotionSettings.lua:196: in function 'promotionSettings.overrideProdVetStatus'
        D:\Test of Time\Scenario\Boudicca\events.lua:157: in function <D:\Test of Time\Scenario\Boudicca\events.lua:156>
    ...e\Scenario\Boudicca\LuaRulesEvents\promotionSettings.lua:196: attempt to call a nil value (field 'overrideProducitonVetStatus')
    stack traceback:
        ...e\Scenario\Boudicca\LuaRulesEvents\promotionSettings.lua:196: in function 'promotionSettings.overrideProdVetStatus'
        D:\Test of Time\Scenario\Boudicca\events.lua:157: in function <D:\Test of Time\Scenario\Boudicca\events.lua:156>
    ...e\Scenario\Boudicca\LuaRulesEvents\promotionSettings.lua:196: attempt to call a nil value (field 'overrideProducitonVetStatus')
    stack traceback:
        ...e\Scenario\Boudicca\LuaRulesEvents\promotionSettings.lua:196: in function 'promotionSettings.overrideProdVetStatus'
        D:\Test of Time\Scenario\Boudicca\events.lua:157: in function <D:\Test of Time\Scenario\Boudicca\events.lua:156>
    
    
    It threw an error while I was going between turns.
     
  12. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    Also I'm trying to use the new function that you provided but this:

    Code:
    if loser.owner == object.tIceni then 
        civlua.createUnit(object.uIceniSwordsmen, object.tIceni, {{gen.getRandomNearbyUnoccupiedTile({loser.location.x,loser.location.y,loser.location.z},4,{0,1,2,3})}}, {count=8,randomize=false, veteran=false})
        end 
    

    throws this:

    Code:
    File number 1 event ending at line 12 successfully parsed.  Event number is 1.
    ...est of Time\Scenario\Boudicca\LuaCore\civluaModified.lua:116: bad argument #1 to 'getTile' (number expected, got civ.tile)
    stack traceback:
        [C]: in function 'civ.getTile'
        ...est of Time\Scenario\Boudicca\LuaCore\civluaModified.lua:116: in function <...est of Time\Scenario\Boudicca\LuaCore\civluaModified.lua:114>
        (...tail calls...)
        ...est of Time\Scenario\Boudicca\LuaCore\civluaModified.lua:137: in function 'civluaModified.createUnit'
        ...ime\Scenario\Boudicca\LuaTriggerEvents\triggerEvents.lua:188: in function 'triggerEvents.unitKilledInCombat'
        D:\Test of Time\Scenario\Boudicca\events.lua:199: in upvalue 'doOnUnitDefeatedInCombat'
        D:\Test of Time\Scenario\Boudicca\events.lua:262: in function <D:\Test of Time\Scenario\Boudicca\events.lua:245>
    
     
  13. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    I'm going to need all the event code, because they typo overrideProducitonVetStatus isn't in my promotionSettings.lua or promotion.lua (I thought this was corrected at some point already, but maybe the fix got overlooked, or I sent the wrong file, or something).

    2 things:

    1.
    civlua.createUnit accepts tiles only in the form {xVal,yVal,zVal}, while gen.getRandomNearbyUnoccupiedTile provides a tileObject instead.

    2.
    gen.getRandomNearbyUnoccupiedTile({loser.location.x,loser.location.y,loser.location.z},4,{0,1,2,3})

    Is being used incorrectly. The first argument 'tile' expects a tileObject, not a coordinate triple, so loser.location is what is needed (I can fix it to accept a coordinate triple, which I did for a lot of other tile functions in the General Library). Also, the allowed tiles should be in the form {[0]=true,[1]=true,[2]=true,[3]=true}. However, I'll change that to just a list of allowed terrains, since that is the more natural way to provide the info as an end user. I'll provide the update shortly.
     
  14. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    Here are the fixes.

    @Dadais

    This copy (and all future copies) of the General Library will regress gen.getRandomNearbyUnoccupiedTile, (and one or two other functions I used to build it) by changing the way the allowedTerrain table works. If you've made use of that function, you'll have to fix it. If you've made extensive use already, let me know, and I'll write you some custom code to help.
     

    Attached Files:

    Dadais likes this.
  15. Dadais

    Dadais Warlord

    Joined:
    Oct 20, 2010
    Messages:
    230
    Location:
    France
    Much thanks Prof.

    I used so far either a (list of) defined location, "the winner's" location, or a half-homemade function calling (Xmin, Xmax, Ymin, Ymax, region*1, tribe, occasion*2) to return a table of locations.
    *1 "string", referring to the TileWithinRegion function calling (Tile, region)
    *2 "string", allowing to personnalize the search (test for each tile by terrain type, city existence and further).
    No worry then, while I see the interest and power of this function ;)
    Just didn't use it yet
     
  16. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    Ok, so I changed it to this:

    Code:
    if loser.owner == object.tIceni then 
        civlua.createUnit(object.uIceniSwordsmen, object.tIceni, {{gen.getRandomNearbyUnoccupiedTile({loser.location},4,{[0]=true,[1]=true,[2]=true,[3]=true})}}, {count=8,randomize=false, veteran=false})
        end 
    
    And received this:

    Code:
    File number 1 event ending at line 12 successfully parsed.  Event number is 1.
    ...est of Time\Scenario\Boudicca\LuaCore\generalLibrary.lua:523: Table did not correspond to tile coordinates
    stack traceback:
        [C]: in function 'error'
        ...est of Time\Scenario\Boudicca\LuaCore\generalLibrary.lua:523: in function 'generalLibrary.toTile'
        ...est of Time\Scenario\Boudicca\LuaCore\generalLibrary.lua:3211: in function 'generalLibrary.nearbyUnoccupiedTiles'
        ...est of Time\Scenario\Boudicca\LuaCore\generalLibrary.lua:3236: in function 'generalLibrary.getRandomNearbyUnoccupiedTile'
        ...ime\Scenario\Boudicca\LuaTriggerEvents\triggerEvents.lua:188: in function 'triggerEvents.unitKilledInCombat'
        D:\Test of Time\Scenario\Boudicca\events.lua:199: in upvalue 'doOnUnitDefeatedInCombat'
        D:\Test of Time\Scenario\Boudicca\events.lua:262: in function <D:\Test of Time\Scenario\Boudicca\events.lua:245>
    
    I guess what I don't understand is this part - how can I use getRandomNearby... in a civlua.createUnit then? Am I completely missing the boat?

    What do I need to do to correct this?

    Attached are all the events as you requested them - I won't work them again until you return them.
     

    Attached Files:

  17. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    Attached is the fix for the production error.

    Here's how you would combine the two functions:
    Code:
    local randomTile = gen.getRandomNearbyUnoccupiedTile(loser.location,4,{0,1,2,3})  -- note that I changed the function to use the 'list of numbers' approach instead of [key]=true approach
    civlua.createUnit(object.uIceniSwordsmen, object.tIceni, {{randomTile.x,randomTile.y,randomTile.z}}, {count=8,randomize=false, veteran=false})
    
     

    Attached Files:

    Dadais and JPetroski like this.
  18. JPetroski

    JPetroski Deity

    Joined:
    Jan 24, 2011
    Messages:
    3,378
    Thanks - that got it to work. In testing, I note that all of the units now go to that single random tile - is there any way to have a "splash" effect where basically they'll spawn at a random tile that is centered on the action? What I'm trying to use this for here is to have a situation where if the Iceni kill certain units, their rebellion grows and grows. So basically I'm looking for a "splattering" effect where warriors from the nearby area start popping up in multiple tiles.

    For example, just to test this--all 96 of these wind up on the same tile:

    Code:
    civlua.createUnit(object.uIceniSwordsmen, object.tIceni, {{randomTile.x,randomTile.y,randomTile.z}}, {count=8,randomize=true, veteran=false})
        civlua.createUnit(object.uIceniNobles, object.tIceni, {{randomTile.x,randomTile.y,randomTile.z}}, {count=4,randomize=true, veteran=false})
        civlua.createUnit(object.uIceniSpearmen, object.tIceni, {{randomTile.x,randomTile.y,randomTile.z}}, {count=12,randomize=true, veteran=false})
        civlua.createUnit(object.uIceniSkirm, object.tIceni, {{randomTile.x,randomTile.y,randomTile.z}}, {count=4,randomize=true, veteran=false})
        civlua.createUnit(object.uIceniArchers, object.tIceni, {{randomTile.x,randomTile.y,randomTile.z}}, {count=4,randomize=true, veteran=false})
    
     
  19. Dadais

    Dadais Warlord

    Joined:
    Oct 20, 2010
    Messages:
    230
    Location:
    France
    That's normal.
    On the same line, each "civlua.createunit" have only one tile location given by these three variables. All unit then randomize locations between only one.
    To have a chance for a different tile between both lines, you would need to launch the gen.getRandomNearbyUnoccupiedTile() function and allocate its result to randomTile again

    In order to have more than one tile, you would need to create a loop :
    something like
    --
    local randomTile = {}
    local RandomTiles ={}

    for i=0 , number of random tiles you wish,1 do
    randomTile = gen.getRandomNearbyUnoccupiedTile(loser.location,4,{0,1,2,3})
    RandomTiles/[i/] = {randomTile.x,randomTile.y,randomTile.z}


    end
    civlua.createUnit(object.uIceniSwordsmen, object.tIceni, RandomTiles, {count=8,randomize=false, veteran=false})

    --
    * /[ and /] are [ and ]
    Prof. Garfield may correct me through ?
     
    Last edited: Nov 28, 2020
  20. Prof. Garfield

    Prof. Garfield Deity Supporter

    Joined:
    Mar 6, 2004
    Messages:
    3,131
    Location:
    Ontario
    As @Dadais correctly points out, you're only getting a random unoccupied tile once, and using it for all locations, and that to get a different tile, you'll have to use gen.getRandomNearbyUnoccupiedTile again.

    This Could, however, raise an issue that you might run out of such tiles (then randomTile will be nil, and cause an error; you should probably guard against that somehow anyway). You might use,

    -- gen.nearbyUnoccupiedTiles(tile,distance,allowedTiles) --> table
    -- returns the table of nearby unoccupied tiles
    -- Indices start at 1 without gaps, but tiles are in no particular order
    -- tile is that you want to find other tiles near to (on same map)
    -- distance is the number of squares away that you can search
    -- allowetiles is either a table of integers such that a tile is acceptable if
    -- possibleTile.terrainType % 16 appears as a value in the table
    -- or a function allowedtiles(possibletile)-->bool
    -- that returns true if the tile is allowed, and false if not

    This will give you a table of all eligible tiles, (key values start at 1, and have no gaps), so you can choose some tiles, and decide which ones to double stack, if necessary.
     
    Dadais likes this.

Share This Page