Need help with new promotion effect

CyrusOrlandeau

Everescan
Joined
Feb 8, 2008
Messages
82
Location
Lost in my head.
Okay, I'm just starting to learn how C++ and python work together, and after my first experiment went well I'm trying to create something new, sort of.
This is the effect I want:

Randomly grants the ____ promotion [to units in the stack] ( _% chance each turn)

The words in the [ ] is what I'm adding to an already pre-existing effect in FF. This means I want a random chance for each unit in the same tile (as the unit that has the promotion with this effect) to get ___ promotion.
Say the name of the promotion I'm working with is Diseased.

Randomly grants the Diseased promotion [to units in the stack] ( 40% chance each turn)
Meaning each unit in the stack has a 40% chance to contract the disease.

I'm looking at CvInfos right now, and I was originally going to start with the PromotionRandomApply tag that the Crazed promo works with, since that's the closest to what I want. Unfortunately, I'm a little lost on how I should alter the effect to the example above. Can someone help point me in the right direction on what to do next? Let me know if you need to see the code for the PromotionRandomApply tag in CvInfos.
Also, later on, I'd like to switch up the effect to allow for more instances, such as % chance to give x promotion after combat. But I need help with the example above first please. :D

-A9A
 
That might work, the reason I wanted to go through the main files to create a specific tag for this effect is because I plan on using it frequently, and I think (I could be wrong) it would cause more lag to work through the python every single time.
I could be wrong, but either way I wanted to get some good hands-on experience with C++ in Civ as well as helping me create my own mod for it. :D

-A9A
 
Yes, DLL is always the better choice (In my DLL Junky Opinion).

You want to clone PromotionRandomApply and iPromotionRandomApplyChance EXACTLY for CvInfos, though honestly that one isn't written in the absolute best possible manner as it only allows you to do a single promotion at a time. But for starters it is what you would be best aiming at so you don't jump in over your head immediately. So you would just rename those two to something like iStackPromotionRandomApplyChance & StackPromotionRandomApply. But other than adding the word "Stack" your work in CvInfos will be exactly the same as is already done.

In CvGameTextManager you will also have the exact same work to do, the only difference will be that your TXT_KEY will need to be different, and in that TXT_KEY you will state that it applies to the stack instead of (or in addition to) the unit.


Where things will be different is in CvUnit::doTurn() where the promotion has a chance to apply. There we have to know precisely how you want it to work. Is it a 10% chance per unit, so in a stack of 10 units I will be able to assume I will hit 1 of them per turn, or is it a 10% chance per turn, and if it fires you hit the whole stack at once?

Either way, what you need is a loop over all units in the stack. For that you do something along these lines:

Code:
	CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode();
	while (pUnitNode != NULL)
	{
		CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
		pUnitNode = plot()->nextUnitNode(pUnitNode);

Then you apply your promotion to pLoopUnit, or do your random check THEN apply it to pLoopUnit.

Also don't forget that it is possible to have enemy units (invisible) and friendly units on the stack. If you don't want them to be able to be hit you'll have to check for matching owners, or at least matching teams.
 
Yay! Thank you xienwolf! I don't know where I'd be without your tutorial and your aid! :lol:

Much appreciated, I was starting to have an EGO moment (eyes glaze over) when I was trying to think what I needed to find. :crazyeye: :run:

Is it a 10% chance per unit, so in a stack of 10 units I will be able to assume I will hit 1 of them per turn, or is it a 10% chance per turn, and if it fires you hit the whole stack at once?
The former, meaning each unit has an individual chance per turn to gain said promotion, so some may get it faster than others. I might want to use the latter effect later on, but not at the moment.

I'll either edit this post or just post again if I come across any more problems. ;)

-A9A
 
Ok, this is what I tweaked from the PromotionRandomApply (renaming :rolleyes:) in CvUnit. Where do I apply the bit you posted onto this so that it loops the unit stack?

Code:
		    if (GC.getPromotionInfo((PromotionTypes)iI).getStackPromotionRandomApply() != NO_PROMOTION)
			{
                if (!isHasPromotion((PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getPromotionRandomApply()))
				{
                    if (GC.getGameINLINE().getSorenRandNum(100, "Promotion Random Apply") <= GC.getPromotionInfo((PromotionTypes)iI).getRandomApplyChance())
					{
                        setHasPromotion(((PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getPromotionRandomApply()), true);
					}
				}
			}

Also, if it's not too much trouble, how do I go about putting the checks for whether a unit is friendly or an invisible hostile? I don't need it at the moment, but later on I will want to be able to differentiate. :D I'd imagine I would need to create another tag to fit in the XML array so that it can work with it, right?

-A9A
 
Code:
            if (GC.getPromotionInfo((PromotionTypes)iI).getStackPromotionRandomApply() != NO_PROMOTION)
            {
                if (!isHasPromotion((PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getPromotionRandomApply()))
                {
                    if (GC.getGameINLINE().getSorenRandNum(100, "Promotion Random Apply") <= GC.getPromotionInfo((PromotionTypes)iI).getRandomApplyChance())
                    {
                        setHasPromotion(((PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getPromotionRandomApply()), true);
                    }
                }
            }

First line is checking that your promotion does something. So you want to keep that, but target your new field you added instead. You probably got that much.

Second line (Note, I am skipping the lines which are just a curly brace) is checking if the unit already has this promotion, as he doesn't need to try and self-apply it in that case. You want to start changing things here.

So you'll keep the first line, then immediately start a loop over all units on the same plot as the current unit. After the bit I posted, (where you have now got pLoopUnit defined) you will do these same checks as the original function does, but the first line will check pLoopUnit to see if they already have the promotion, nextline will do a random check against your new random value instead of the one you copied from, and last line will apply the promotion to the pLoopUnit instead of to this unit.

If you want it to only apply to units which you personally own, you would check

Code:
if(getOwner() == pLoopUnit->getOwner())
. Same kind of check for any other conditions you want to use. I would place all of these BEFORE the check for pLoopUnit->isHasPromotion. You might want to check getTeam() instead of getOwner(), and possibly check
Code:
if (this != pLoopUnit)
if you don't want to allow the unit to apply the promotion to itself.
 
Sorry for the late response, life decided to pitch another curveball at me again. :rolleyes:

Xienwolf, your last response confused me a bit, but that's very likely my fault, not yours. I only barely started your tutorial (haven't even made that first boolean tag yet) before my mind went awol and wanted to do something else, and then later reasoned with me that hands-on experience is just as good, and after this tag than everything else I wanted could be easily done in XML... so, yeah, for some reason my attention span has decreased ten-fold since I'm not in school anymore. :crazyeye:

Anyways, may I please request exactly where I need to place the extra code I want in all this? Don't get me wrong, I'm working on it myself as I write this I don't expect you to do all the work for me in any way, but for some reason I always hit walls when something goes wrong and I try to fix what I know very little about. :cringe:

-A9A
 
I'm just slapping the two pieces of code I have posted here together, so some format issues may exist, but you want something like this:

Code:
            if (GC.getPromotionInfo((PromotionTypes)iI).getStackPromotionRandomApply() != NO_PROMOTION)
            {
     CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode();
    while (pUnitNode != NULL)
    {
        CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
        pUnitNode = plot()->nextUnitNode(pUnitNode);

                if (!pLoopUnit->isHasPromotion((PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getStackPromotionRandomApply()))
                {
                    if (GC.getGameINLINE().getSorenRandNum(100, "Stack Promotion Random Apply") <= GC.getPromotionInfo((PromotionTypes)iI).getStackRandomApplyChance())
                    {
                        pLoopUnit->setHasPromotion(((PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getStackPromotionRandomApply()), true);
                    }
                }
            }
            }


This doesn't check if you own the unit or not though, so to do that you would modify it to look like this:

Code:
            if (GC.getPromotionInfo((PromotionTypes)iI).getStackPromotionRandomApply() != NO_PROMOTION)
            {
     CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode();
     while (pUnitNode != NULL)
     {
         CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
         pUnitNode = plot()->nextUnitNode(pUnitNode);

                if (pLoopUnit->getOwner() == getOwner())
                {
                if (!pLoopUnit->isHasPromotion((PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getStackPromotionRandomApply()))
                {
                    if (GC.getGameINLINE().getSorenRandNum(100, "Stack Promotion Random Apply") <= GC.getPromotionInfo((PromotionTypes)iI).getStackRandomApplyChance())
                    {
                        pLoopUnit->setHasPromotion(((PromotionTypes)GC.getPromotionInfo((PromotionTypes)iI).getStackPromotionRandomApply()), true);
                    }
                }
            }
              }
              }

And now it will only apply the promotion to units which you own.
 
Bless you xienwolf. Now I'll have a better idea how to stuff like this because I can use this as an example. This tag opens up a lot of doors for me, and I hope to post my work here soon so you guys can enjoy it too. :D

I'll edit this post if there are any more problems.

-A9A
 
No! Never edit :) If you make a new post then it bumps the thread and I notice it. If you edit, I never check this post again and your question goes unanswered.

ok....:lol:
yay, i get to see your sense of humor now! :lol:

i'm about to try the code now, crossing my fingers....

-A9A
 
it didn't load up at first, but that was because I forgot about the schema and adding it in there, with all the debugging it got replaced before. XDXP

Unfortunately, it doesn't work, but the game functioned fine otherwise (not something I'm used to. XD) So now we try to figure out why it's not working, right?
Here's what I'm thinking: The game functioned fine except there was no extra effect. So I don't think I typo'd the stuff in CvInfos, CvGameTextManager, or CvUnit. Although the text for the effect didn't show up in the test promo, so I may have messed up the Cvgametextmanager, I'll go check. And I'll check to see if I made any typos with the other two.

EDIT: As I thought, I forgot to add a text entry for the promo. :blush: Ooops.
Still checking the others for typos

-A9A
 
Troubling. It doesn't work, and even after fixing the text entry and checking for typos (there weren't any) I still can't even see the description of the effect.
Well, I'm stumped with this now. Where should we go from here xienwolf?
 
Now you force the code so you can see precisely where things broke at.

You made 2 new functions in CvInfos, CvPromotionInfos::get____(), both of them return an Integer value, correct? Set it to say:

return 5;

instead of

return m_i____;

Then run the game and see what you see. Ideally, EVERY promotion in the game will claim to have a 5% chance to apply the 6th promotion in your CIV4PromotionInfos.xml file to the units in their stack.

If that is the case, then your text key works, but your loading from the XML doesn't. If that statement does NOT show up, then your text key is a failure, but run the game and see if units with promotions attempt to apply the 6th listed promo to their stack or not. If they try to, your only problem is the text key (you might want to have the chance to apply return a 100 though to make this portion of the testing easier, then you know they should ALWAYS be trying to apply the promotion).

If nobody tries to apply the promotion, then THAT part of the code is also broken.


Once you know which file holds broken code, it becomes easier to figure out in what way it broke through similar actions, replace something which asks for a variable with a raw number so that you know it will ALWAYS get that specific number, and see if things work as expected from there in.
 
I was a little confused at first, but I get it now. Yay I'm learning! ^_^
I changed out the code to return 5, testing it out as soon as I clear up some processor speed (12 things running at the moment, :rolleyes:)

Why would it apply the 6th listed promotion in the PromotionInfos? That wasn't listed in our changes, which means its some kind of default thing, right? I'm wondering where and what code is causing it to do that, just trying to find out. ;)

EDIT: Okay, this is what I did:

Original
Code:
int CvPromotionInfo::getStackPromotionRandomApply() const
{
	return m_iStackPromotionRandomApply;
}
New
Code:
int CvPromotionInfo::getStackPromotionRandomApply() const
{
	return 5;
}
Is this what you meant or am I taking your direction too literal? I think I did something wrong because the game won't even load up now. :rolleyes:

-A9A
 
My mistake, the game did load, it just took 30 minutes to do so, even after I closed processes.
Well, the text still doesn't show and the effect still doesn't work. Tweaking a few loose ends in the xml to see if it will make a difference right now. I doubt it, but might as well fix what I can.
Did I misinterpret your meaning on the code? Slowly but surely I'm trying to improve what I know and reduce errors, and I want to thank you for helping me. Seriously. ^_^


-A9A
 
It would show you the 6th listed promotion because the first listed promotion is "0" and the NEXT one is "1"

You did do half of exactly what I stated, and did it right :)


Could you
Code:
 post your CvGameTextMgr.cpp changes?  I am pretty sure that you check for the Chance > 0, rather than the Promotion != -1, so making JUST the change you listed wouldn't be enough to force the text key to show up.

Also, make sure to REALLY check your code for any typos in the <PromotionStackApply> of the XML against the CvPromotionInfos::read(pXML) section of the DLL where you listed something in quotes "PromotionStackApply"  If those are off then the XML won't be read, but no errors will show up anywhere.
 
That seems a bit obvious, and not what I meant, but ok. I'll go with the flow. :lol:

Good to know I did something right. :D So what's the other half I missed?

Okay, here's what I added/changed to the gametextmanager:
Code:
    if (GC.getPromotionInfo(ePromotion).getStackPromotionRandomApply() != NO_PROMOTION && GC.getPromotionInfo(ePromotion).getStackRandomApplyChance() != 0)
    {
        szBuffer.append(pcNewline);
        szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_STACK_RANDOM_APPLY", GC.getPromotionInfo((PromotionTypes)GC.getPromotionInfo(ePromotion).getStackPromotionRandomApply()).getDescription(), GC.getPromotionInfo(ePromotion).getRandomApplyChance()));
    }
This was the only thing I added because there was only one mention of the promotionrandomapply code there, so I copied it to add this.

btw, just to let you know, I took your advice and used the /** **/ headliner to label all my changes so I know everything I did. So I did learn something from your guide! :lol::lol::lol:

-A9A
 
The other half was forcing getStackRandomApplyChance() to also be a non-zero number.

Since your gametext demands that both of these two values are non-default, that is why you aren't seeing the text right now. Most likely.
 
Top Bottom