Convenient Start Mod

manekk

Chieftain
Joined
Mar 18, 2015
Messages
59
It's a common knowledge that the starting location and first city placement matters.
At the same time there is a substantial luck factor involved in where you begin your adventure.

On some occasions Sid Meier spawns you in a really nice location and your civilization's dream about future prosperity has a big chance to come true. And on some others there's literally nothing around you - you're at the edge of a desert, one luxury resource to grab, almost non existing hills and fertile tiles, everybody else has got marble or salt, and your promised land is just 3 tiles way.

It is quite frustrating in single player games, but you can either treat it as additional challenge and handicap on top of the difficulty level, or waste your time on reloading your game with all the 100 mods installed.
In multiplayer games though it is very unfair if only one player starts with such a challenge, and (assuming you all agree) restarting the game takes sooo much time.

This mod is for those of you who want to avoid the luck accusations in MP, or just more flexibility in SP.

After applying this mod your starting settler (and only the starting settler) will have the following perks for the 1st (and only 1st turn of the game):
- Air Recon promotion, so that you can immediately assess your starting conditions and tiles around you and not be limited by the mountains
- 4 moves, so that you can actually move from the crappy starting place and still settle on 1st turn
- Ignore Terrain Cost, so that this annoying jungle don't stop you

In order to block potential abuse of uncovering a lot of map on the expense of not founding 1st city immediately, the starting settler loses all his abilities after 1st turn.

This mod is fully compatible with all the other mods known to me, including:
- MPMPM
- all Mod Civilizations (including the ones replacing Settlers - tested on Haiti)

The only incompatibility appears when other mods remove the "Future Technology" from the game or when you don't start with a Settler.

Credits:
SushiSquid/Joe Danger for the original SuperSettler concept
Nutty - sql and lua coding
Serp - creative discussion

Steam workshop link: https://steamcommunity.com/sharedfiles/filedetails/?id=426714418
 

Attachments

  • Convenient start (v 2).civ5mod
    2.3 KB · Views: 697
Your 2 options would be to use Lua to grant the Super Settler to all civs instead of using the Civilization_FreeUnits table, or use a SQL trigger to make corresponding updates to the Civilization_FreeUnits table every time the Civilizations Civilization_FreeUnits table is updated.

EDIT: As far as a SQL Trigger, something like:
Code:
CREATE TRIGGER ConvenientStart
AFTER INSERT ON Civilization_FreeUnits
BEGIN
  UPDATE Civilization_FreeUnits SET UnitClassType = 'UNITCLASS_SUPERSETTLER'
    WHERE UnitClassType = 'UNITCLASS_SETTLER';
END;

And note that your where clause for the Super Settler (at the end of your code snippet) should probably be <Where UnitClassType="UNITCLASS_SETTLER"/> or maybe <Where UnitAIType="UNITAI_SETTLE"/> instead of <Where count="1"/> to be compatible with mods that change starting units.

EDIT #2: [Changed above to an update rather than an insert with the trigger based on the FreeUnits table itself, since all of the modded civs should have added SOMEthing there already.]
 
Thank you for your quick help Nutty

First of all - it works perfectly! I have added the sql trigger to the mod and now Convenient Start works also for mod civs.
Of course I made it more difficult for myself at the beginning, as I have decided to remove from the.xml file:
<Civilization_FreeUnits>
<Update>
<Set UnitClassType="UNITCLASS_SUPERSETTLER"/>
<Where count="1"/>
</Update>
</Civilization_FreeUnits>

thinking that sql will deal with it anyway. It didn't and I had to reverse the change :)

You're also right about the update clause - I have left the original code <Where count="1"/> being under naive assumption that there is always 1 starting free Settler, whereas I should have just changed the Settler to Supersettler, which is more universal.

I will release version 2 tomorrow with clear credits given to you.

That clarifies priority 1!

Would you have any ideas regarding priority 2? I guess this would require a complicated .lua file. I have seen a solution in "Militia Defense" where Militia would disband after 20 turns, and I'm guessing this would probably be something similarly difficult.
But maybe there's a chance it's as easy as your first suggestion :)
 
Priority 3 is a toughie, but #2 is a small bit of Lua, something like:
Code:
function RemoveSuperSettlers(iPlayer)
	local turn = Game.GetElapsedGameTurns()
	if (turn == 1) then
		local pPlayer = Players[iPlayer]
		for unit in pPlayer:Units() do
			if (unit:GetUnitType() == GameInfoTypes["UNIT_SUPERSETTLER"]) then
				pSettler = pPlayer:InitUnit(GameInfoTypes["UNIT_SETTLER"], unit:GetX(), unit:GetY(), GameInfoTypes["UNITAI_SETTLE"], unit:GetFacingDirection())
				pSettler:Convert(unit)
			end
		end
	else
		if (turn > 1) then
			GameEvents.PlayerDoTurn.Remove(RemoveSuperSettlers)
		end
	end
end
GameEvents.PlayerDoTurn.Add(RemoveSuperSettlers)

[NOTE: Not tested!]

EDIT: Changed from GetGameTurn to GetElapsedGameTurns to allow for advanced starts.
 
I have tested your solution and unfortunately it didn't work.

I have included in the mod additional .lua file with the suggested code, started the game, and on turn 0 (as expected) I was given a SuperSettler. I have moved around not founding a city and pressed end turn. Next turn the event should be triggered, but nothing happened. And next turns as well.

Since I am learning by doing, I have invested my next two hours in google search and trying different things, from simple to less simple.

I replaced GetElapsedGameTurns with GetGameTurn - didn't work.
I replaced GameEvents.PlayerDoTurn with GameEvents.ActivePlayerTurnStart - didn't work.
Then I started to analyse the code, but it seemed right from the logical point of view:
- if turn=0 it does nothing
- if turn=1 it implements the change, by converting the identified SuperSettler to a regular Settler, for a given player in a given location
- if turn>1 it removes the function as no longer needed

So if the logic and structure is ok, the things I thought could create the problem could be:

1. (unit:GetUnitType() == GameInfoTypes["UNIT_SUPERSETTLER"]) doesn't identify the unit properly - I have also tried to rewrite the mod to change the starting warrior to a swordsman - and it didn't work.

2. Convert function changes SuperSettler to Settler, but keeps his promotions and movement rate - but I sort of eliminated it, as for the sake of testing I changed the outcome unit to a Scout, and the SuperSettler didn't change into it.

3. Convert function doesn't work?

Would you have any ideas what might have gone wrong?
Would creating a function of killing the SuperSettler and replacing him with a brand new Settler help?
Or maybe there is a way to set a default upgrade from SuperSettler to Settler, and execute an upgrade with losing all the promotions (the new one shouldn't have air recon and no terrain movement cost)?
 
hey manekk :)

If you have some simple questions about lua coding, you can also try asking me. Maybe I know the answer, because I spend my last weeks at writing several lua scripts and asking alot of questions here at the forum :D

About the topic of starting positions:
There is a mod, that let's you choose your starting position, by switching to all existing positions. Unfortunately there is no way to hide the positions you switch by again. (you can see all positions with fog of war).
And I did not test, if it works in multiplayer...
But I think it is easier to solve point 1) and 2) from the speedy settler :D

To your point 3):
I don't think it is necessary to teach the AI something. Because I think the starting positions are calculated by your computer. And with the same logic, the AI chooses their starting places. So even if you tell them, they are free to move around 1 turn, they would settle at the starting point. (not sure if this would really happen, but I think you should spend your time at more important things ;) )

I also included a changed version of speedy settler in my modpack some months ago. (6 move, ignore terrain and +1 sight range). But of course, if you manage to create a more balanced version, I will use your version :)
 
I'd guess that you didn't add an Entry Point for the Lua file (i.e., InGameUIAddin). See http://forums.civfanatics.com/showthread.php?t=487846

Duh... Feel pretty stupid now :blush:
Thanks, of course it helped. Well, learning is a hard process, and if I knew how to do things, I wouldn't be asking :)

Now there's only one thing remaining from priority 2, if you still have the patience Nutty...

As expected, on turn 1 the SuperSettler turns into a normal Settler, which is great. Unfortunately the new Settler keeps all the promotions from SuperSettler, and additionally only in turn 1 it still keeps 4 moves. I guess the Convert function keeps all the starting conditions of the previous unit on the turn, and all the promotions.

Is there an easy way of changing the SuperSettler into a brand new copy of a Settler, that has no perks from the previous incarnation, immediately when turn 1 starts?

Since I am just starting to mod, I thought I have a brilliant idea:
I have created two new promotions: AIR_RECON2 and IGNORE_TERRAIN_COST2, and added them to <UnitPromotions>. They were exact copies of the normal promotions, but I have added <LostWithUpgrade>true</LostWithUpgrade> line. Then I gave SuperSettler these two new promotions instead of the regular ones, hoping it will lose them after Convert. I have started the game being so damn proud of my idea, and of course as a result the SuperSettler didn't appear at all :) I guess I still have a lot to learn.

@Serp

Thanks for your offer of helping. I will be still asking for it in public threads hoping for more attention, but you're more than welcome to help me whenever you feel like it.

Regarding unfair starting position - SuperSettler seems to do exactly what is needed for my multiplayer games. I don't want to make it more complicated than it has to be.
The original Speedy Settler was a good base idea, but I am trying to test and get opinions on all the mods I'm using, and the outcome/feedback was:
- it doesn't work with any new CIvs from mods (major problem, fixed thanks to Nutty - works like a charm now)
- 10 or even 6 moves allows you quite often to actually block in the first turn a minor civ on small and medium maps, and on duel maps even to block your opponent (major problem, easily fixed - 4 moves never caused a problem yet)
- +1 sight range doesn't help a lot, as you still can't see through mountains, or two hills. First city placement is very important in MP games, and I want to make it more fair for everybody, reducing the luck factor (somewhat a problem, easily fixed - Air Recon seems to do the trick)
- you can abuse the Settler's skills on the expense of founding your city later, and uncover quite a lot of map (somewhat a problem, addressed now)

Hopefully this mod will be ready soon, and hopefully you'll find it useful. In the end of the day it doesn't make a massive difference, it can only make the start of MP game less luck related.

Regarding the AI - I'm actually not focusing on it all now. AI is already dumb enough in single player, it's even dumber in MP, and AI Smart unfortunately doesn't help at all (stopped using it, as it improves few things on the cost of some others being worse than in original AI). I have massive expectations towards Artificial Unintelligence, and keep fingers crossed for the author.
 
@manekk:
Lost with upgrade is a good idea, but makes things more complicated if you plan to use mods which handle with promotions..

So since you already use a lua, it would be better to simply use this lua ;)
Two possible ways:
1) Delete the whole supersettler and place a normal settler at this position, instead of converting it.
2) Convert the Supersettler and simply remove the promotions.

I think 2) is the easier one. So just change nuttys lua code to the following (added two if conditions which check for promotion and remove them):

Code:
function RemoveSuperSettlers(iPlayer)
	local turn = Game.GetElapsedGameTurns()
	if (turn == 1) then
		local pPlayer = Players[iPlayer]
		for unit in pPlayer:Units() do
			if (unit:GetUnitType() == GameInfoTypes["UNIT_SUPERSETTLER"]) then
				pSettler = pPlayer:InitUnit(GameInfoTypes["UNIT_SETTLER"], unit:GetX(), unit:GetY(), GameInfoTypes["UNITAI_SETTLE"], unit:GetFacingDirection())
				pSettler:Convert(unit)
                                [B]if (pSettler:IsHasPromotion(GameInfoTypes.PROMOTION_IGNORE_TERRAIN_COST)==true) then
                                    pSettler:SetHasPromotion(GameInfoTypes.PROMOTION_IGNORE_TERRAIN_COST,false)
                                end
                                if (pSettler:IsHasPromotion(GameInfoTypes.PROMOTION_AIR_RECON)==true) then
                                    pSettler:SetHasPromotion(GameInfoTypes.PROMOTION_AIR_RECON,false)
                                end[/B]
			end
		end
	else
		if (turn > 1) then
			GameEvents.PlayerDoTurn.Remove(RemoveSuperSettlers)
		end
	end
end
GameEvents.PlayerDoTurn.Add(RemoveSuperSettlers)

edit1:
if a thing you made does not show up in game, it is very likely that there is something wrong in a xml script.

edit2:

I just tested a mod based on what we found out in this thread so far. It is not perfect yet:
- After converting the supersettler, the settler has 4/2 move in this converting round.

So the code has to look like this:
Code:
function RemoveSuperSettlers(iPlayer)
	local turn = Game.GetElapsedGameTurns()
	if (turn == 1) then
		local pPlayer = Players[iPlayer]
		for unit in pPlayer:Units() do
			if (unit:GetUnitType() == GameInfoTypes["UNIT_SUPERSETTLER"]) then
				pSettler = pPlayer:InitUnit(GameInfoTypes["UNIT_SETTLER"], unit:GetX(), unit:GetY(), GameInfoTypes["UNITAI_SETTLE"], unit:GetFacingDirection())
				pSettler:Convert(unit)
                if (pSettler:IsHasPromotion(GameInfoTypes.PROMOTION_IGNORE_TERRAIN_COST)==true) then
                    pSettler:SetHasPromotion(GameInfoTypes.PROMOTION_IGNORE_TERRAIN_COST,false)
                end
                if (pSettler:IsHasPromotion(GameInfoTypes.PROMOTION_AIR_RECON)==true) then
                    pSettler:SetHasPromotion(GameInfoTypes.PROMOTION_AIR_RECON,false)
                end
                [B]pSettler:SetMoves(120) -- 60 is 1 movepoint[/B]
			end
		end
	else
		if (turn > 1) then
			GameEvents.PlayerDoTurn.Remove(RemoveSuperSettlers)
		end
	end
end
GameEvents.PlayerDoTurn.Add(RemoveSuperSettlers)

I think now everything is fine and the mod is complete :)
 
@Serp, why does your second option seem easier? Wouldn't the first simply require changing the line " pSettler:Convert(unit) " to " unit:Kill(false, -1) " and done?
 
@Serp, why does your second option seem easier? Wouldn't the first simply require changing the line " pSettler:Convert(unit) " to " unit:Kill(false, -1) " and done?

I don't know much, I think you know more :D
But only killing the supersettler does not solve the problem. You have to create a new settler at the same position. I'm not sure how creating a new unit works, but I can imagine, that you need the players. So you have to do a for loop or simular.
All in all it is more complex. That's why I think the 2) one is easier.

edit:
ah I missed we already have a loop over all players with PlayerDoTurn... so maybe it would not be that difficult :D But I never did it before, that's why I prefered the solution I have experience in :D The command for placing a new unit is SetPlaceUnit(Unit) ?
 
The immediately prior line creates a new settler with InitUnit(). This is within a loop that goes through all of the player's units until a Super Settler is found. The parameters pSettler:GetX(), pSettler:GetY() get the coordinates to put the new vanilla settler in the same location as the Super Settler. I'm used to using Convert() (which also performs a Kill) rather than a simple Kill(), but of course we don't actually want to carry any promotions over, so that's what I should have used.
 
The immediately prior line creates a new settler with InitUnit(). This is within a loop that goes through all of the player's units until a Super Settler is found. The parameters pSettler:GetX(), pSettler:GetY() get the coordinates to put the new vanilla settler in the same location as the Super Settler. I'm used to using Convert() (which also performs a Kill) rather than a simple Kill(), but of course we don't actually want to carry any promotions over, so that's what I should have used.

Ah, I was not aware the InitUnit() does create a unit . :)

So to post the complete best code:
Code:
function RemoveSuperSettlers(iPlayer)
	local turn = Game.GetElapsedGameTurns()
	if (turn == 1) then
		local pPlayer = Players[iPlayer]
		for unit in pPlayer:Units() do
			if (unit:GetUnitType() == GameInfoTypes["UNIT_SUPERSETTLER"]) then
				pSettler = pPlayer:InitUnit(GameInfoTypes["UNIT_SETTLER"], unit:GetX(), unit:GetY(), GameInfoTypes["UNITAI_SETTLE"], unit:GetFacingDirection())
                unit:Kill(false, -1)
			end
		end
	else
		if (turn > 1) then
			GameEvents.PlayerDoTurn.Remove(RemoveSuperSettlers)
		end
	end
end
GameEvents.PlayerDoTurn.Add(RemoveSuperSettlers)
 
I would have opened champagne, but unfortunately I don't have one :)

Thank you very much for your help in making it happen! The mod works as intended, and I see no further improvements that would be necessary. Of course I may be wrong, so please let me know if you would still see anything that could be improved.

Maybe actually a minor thing, that I was luckily able to implement myself (I tell you, I'll be good in this, like maybe in 5 years ;) )
I was wondering what would happen to a Polynesian SuperSettler after embarking and changing to a regular Settler. He changed, but wasn't embarked anymore and could repeat one of the most famous tricks of Jesus, so I have added one line of code:

pSettler = pPlayer:InitUnit(GameInfoTypes["UNIT_SETTLER"], unit:GetX(), unit:GetY(), GameInfoTypes["UNITAI_SETTLE"], unit:GetFacingDirection())
pSettler:SetEmbarked(unit:IsEmbarked())
unit:Kill(false, -1)

I am going to change the first post to something fancier tomorrow and ask one of the moderator to remove the "help needed" from the title. I'll also add the credits. This wouldn't be possible without your coding skills Nutty, and I very much appreciate the next best solution Serp, which I would totally buy not having the Kill option :)
 
@ Nutty:

How does the sql trigger work ?

I ask because we now have this trigger in sql AND the FreeUnits replacement in xml.
Is this okay? Or should there only be one of these replacements?

I mean we have sql:
Code:
CREATE TRIGGER ConvenientStart
AFTER INSERT ON Civilization_FreeUnits
BEGIN
  UPDATE Civilization_FreeUnits SET UnitClassType = 'UNITCLASS_SUPERSETTLER'
    WHERE UnitClassType = 'UNITCLASS_SETTLER';
END;
and xml:
Code:
<Civilization_FreeUnits>
		<Update>
			<Set UnitClassType="UNITCLASS_SUPERSETTLER"/>
			<Where UnitClassType="UNITCLASS_SETTLER"/>
		</Update>
	</Civilization_FreeUnits>
 
The trigger is only activated when the trigger sees a database change as specified, however the database is already filled with the base game's values, and those of any mod that happen to be activated before your's, so you need both. You might save a couple processor cycles by making sure that XML file is loaded before the SQL (i.e., higher on the list on the Actions tab), though the trigger would have nothing to do so it doesn't make much of a difference.
 
Top Bottom