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

[RaRE] Reworking the combat system

Discussion in 'Civ4Col - Creation & Customization' started by Commander Bello, Sep 10, 2014.

  1. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    Just to let you guys know what I am currently doing.

    Due to the fact that limited combat rounds have been introduced the whole vanilla concept doesn't fit anymore as the fact of limited combat is unknown to the AI (both natives and Europeans).

    Based on this and on the (completely justified) complaints about certain weaknesses of the combat system, especially the overwhelming strength of artillery type units against settlements (and here especially in regards to natives), I had a closer look at the combat system and the internal calculations.

    One of the main functions for the combat system is the
    int CvUnit::maxCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
    revealing some inaccurracies.

    First, it will return an integer value (which later will be used by
    int CvUnit::currCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const,
    which itself delivers an integer, too) which I regard as being a bad choice. This clearly should be changed to a float value.
    One may even think about combining both functions, as the currCombatStr() doesn't do anything else then calling maxCombatStr() and multiplying it with the current hitpoint percentage (which, due to the integer value, will be lower than it should be).

    Second, in the further way of calculating combat results, both aforementioned functions are called several times again, which makes the use of integer values more and more questionable.

    Third, it doesn't take certain special cases into account as there would be:
    terrain specific combat modifiers in combination with city attack, even worse if the city is located on top of a hill.

    Terrain specific combat modifiers are especially meaningful in the case of cavalry type units which, up to now, except for higher speed don't have specific advantages over infantry style units.

    Furthermore, one of the main problems of the combat system is the way in which combat modifiers are calculated (I will come back to this topic in a later posting).

    In short, I am aiming at making the AI attack not only if the chance for winning (= killing the enemy) is good, but also if the chance to survive (which for most units now is a new feature) is sufficient, too.
    This shall especially help the natives to counter attack stacks approaching their settlements.

    I will keep you updated.
     
  2. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,355
    Speaking of combat, I have encountered a problem with RaR 1.9 (still haven't updated due to local modifications). The problem is that my pirate attacks an enemy ship, the game zooms in, the enemy vanishes and the game never zooms out again. I haven't touched any code, which could be related to this. Is it something anybody else has seen?

    I have a savegame, which does it every time, but I haven't got around to debug it yet. It seems quite rare as it is the first time I have seen it.


    Vanilla uses lots of ints instead of floats or doubles because back in the days ints were a lot faster. However calculating with floating points is done in hardware and modern CPUs no longer have this issue. In fact I have benchmarked a CPU, which calculates floats and ints in the same number of cycles.

    As a result, (X*92)/100 is slower today than X*0.92, which mean the vanilla floating point avoidance hurts performance rather than increase. I would say feel free to leave ints if it makes any sense whatsoever.


    The rest of your post looks interesting. Do try to keep your code together and avoid duplicated code as this will make the code more modder friendly. I have a feeling this is something, which will never be fully done as it will need lots of finetuning to get balance right.
     
  3. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    With "the enemy vanishes" you mean the escape feature, right?
    This is called when the respective unit (upto now only cargo ships) has this feature set and "is killed", but has got a lucky draw from the RNG.
    What might cause the effect of not going back to the normal screen is beyond me, though. I have never had such a thing happen to me.
    All I could think of at the moment (without having looked it up in the code) would be that the only city with a harbor (that should be the requirement for the escape feature, IIRC) got lost in that very turn.


    The problem is that ints don't make any sense in combat calculation (and at quite some other places, neither).
    But I will try to keep your remarks in mind. :)


    Well, actually I was thinking of completely outcommenting the respective functions and replacing them with a new version.
    In the maxCombatStrength() there are so many inaccuracies that changing every second line would lead to even more confusion, as far as I see it (and don't get me started talking about other combat calculations - it's just a mess).
     
  4. agnat86

    agnat86 Warlord

    Joined:
    Jun 30, 2011
    Messages:
    218

    I (still) encounter this problem when I attack a rival's last ship, when sinking it would result in that player's withdrawal from the New World.
    I can only "escape" by clicking on a random point of the mini-map, at which point the game zooms out again and travels to that point. It is only then that I get the message that my opponent is defeated.
     
  5. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    Guys, I need your input.

    From the maxCombatStr(), another function int CvPlot::defenseModifier(TeamTypes eDefender, bool bHelp) const is called.
    Inside this function we find this check:
    Code:
    	if (eImprovement != NO_IMPROVEMENT)
    	{
    		if (eDefender != NO_TEAM && (getTeam() == NO_TEAM || [B]GET_TEAM(eDefender).isFriendlyTerritory(getTeam()))[/B])
    		{
    			iModifier += GC.getImprovementInfo(eImprovement).getDefenseModifier();
    		}
    	}
    
    I don't get, why the defensive value of an improvement should only be valid in friendly territory.
    If I can hide behind a wall, this wall doesn't vanish nor does it become less stable if it is placed in enemy or neutral territory, right?

    Unless somebody could indicate why this check might make sense, I am going to change this. :)

    Edit:
    In addition, we have this function:
    Code:
    int CvCity::getDefenseModifier() const
    {
    	[B]if (isOccupation())
    	{
    		return 0;
    	}[/B]
    
    	return ((getTotalDefense() * (GC.getMAX_CITY_DEFENSE_DAMAGE() - getDefenseDamage())) / GC.getMAX_CITY_DEFENSE_DAMAGE());
    }
    I think it is highly questionable why an occupied city shouldn't give you protection from enemy attacks. The city wall does not disappear just because of the occupation, right?
     
  6. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,355
    If plot ownership should be considered, one might argue that plot knowledge plays a role. Imagine conquering a fortress and then the previous owner attacks right away. The enemy will know every single detail of the structure while the conquerer only had a brief view before the new battle.

    If we are going to have ownership as a parameter, then we could count turns a player had a unit in a plot as awareness of strategic positions. Plot owner will then gain knowledge regardless of unit positions. Changing feature or improvement resets this counter, except for plot owner. We could do something like people only get 50% of plot combat bonus and increase that by 5% for each turn a unit examines the plot.

    Come to think of it, it actually makes sense. If you plan an attack, you scout the predicted combat area before you charge to make the best of the terrain features. The same goes for defense. If you know the terrain well, you know how to quickly go to the hill, which looks like a makeshift fortress, which gives you a defense bonus.
     
  7. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    That's exactly the case described in my edit which you may have missed as our postings seem to have been written in the same moment.


    Ok, I will leave this for a second or third step.
    At the moment I am still busy with trying to simplify things. :)

    Edit:
    taking your input I will try to change the city defense value according to occupation time.
     
  8. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    Talking about the defense modifiers for cities, I would like to ask if we shouldn't allow native cities to be conquered, too (think of Mexico City and Tenochtitlan).
    This would require a setting for civilizations to exclude nomadic tribes from that feature. In other words, Mayan or Aztec cities could be conquered, Apache or Sioux settlements could not.

    What do you think?
     
  9. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,355
    I like the concept of conquering native cities, but we need to think this through. Natives have different buildings (such as totempoles) and actually very few buildings. I tried making natives playable at one point and I recall crashes. I think it was due to lack of GUI code for native only buildings. If it should make sense to conquering cities, they should have interesting buildings.

    Also we would end up with a city with no citizens as we do not conquer the native units. That could be fixed by adding a native slave as a citizen.
     
  10. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    Well, conquering native cities would be a future step of course.
    Nevertheless, as far as I recall, we have a probability for conquering buildings. If this would be set to "0" for native specific buildings, we shouldn't run into problems in this area.
     
  11. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    Meanwhile, I am getting an impression why people never really have modded the combat system.
    For all who want to have a laugh, look at the spoiler, for the others it is not that interesting...
    Spoiler :

    Ok, so we have the CvUnit::maxCombatStr(...), calculating each and everything (which has to be done, of course).

    What is missing is the adjustment to the actual "health" of the unit. This is done in
    CvUnit::currCombatStr(...)
    Code:
    int CvUnit::currCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
    {
    	return ((maxCombatStr(pPlot, pAttacker, pCombatDetails) * currHitPoints()) / maxHitPoints());
    }
    
    So far, so good. Question at this point is whether it is really necessary to have maxCombatStr and currCombatStr as seperated values?
    Well, let's have a look.

    There is a function
    Code:
    int CyUnit::maxCombatStr(CyPlot* pPlot, CyUnit* pAttacker)
    {
    	return m_pUnit ? m_pUnit->maxCombatStr(pPlot->getPlot(), pAttacker->getUnit()) : -1;
    }
    but this function reveals no calls when checked.

    Exactly below that function is the corresponding
    Code:
    int CyUnit::currCombatStr(CyPlot* pPlot, CyUnit* pAttacker)
    {
    	return m_pUnit ? m_pUnit->[B]currCombatStr[/B](pPlot->getPlot(), pAttacker->getUnit()) : -1;
    }
    
    which doesn't reveal any calls, either.

    Ok, let's try it in a different way.
    The combat itself is calculated in
    Code:
    void CvUnit::resolveCombat(CvUnit* pDefender, CvPlot* pPlot, CvBattleDefinition& kBattle)
    {
    ...
    	int iAttackerStrength = [B]currCombatStr[/B](NULL, NULL, &cdAttackerDetails);
    	int iAttackerFirepower = currFirepower(NULL, NULL);
    ...
    	getDefenderCombatValues(*pDefender, pPlot, iAttackerStrength, iAttackerFirepower, iDefenderOdds, iDefenderStrength, iAttackerDamage, iDefenderDamage, &cdDefenderDetails);
    
    And now things are getting interesting.

    Let's have a look at currFirepower:
    Code:
    int CvUnit::currFirepower(const CvPlot* pPlot, const CvUnit* pAttacker) const
    {
    	return (([B]maxCombatStr[/B](pPlot, pAttacker) + [B]currCombatStr[/B](pPlot, pAttacker) + 1) / 2);
    }
    
    Not only does this function call the maxCombatStr twice, once directly and once indirectly via currCombatStr, but the return value is just... let's say, questionable.
    Say the maxCombatStr returns a value of "10" and the actual hitpoints are 87 out of 100.
    Then we get a value of "10" (maxCombatStr) + "8" (!!! - currCombatStr is an int function!), to which we add "1", so the preliminary result is "19" which now we divide by "2", giving us a final result of "9", as this function again is an integer function.

    So, at the moment we have two values allocated to the attacker, the currCombatStr and the currFirepower.
    maxCombatStr now has been called three times, once for currCombatStr and twice for currFirepower.

    Let's have a look now at the
    Code:
    void CvUnit::getDefenderCombatValues(CvUnit& kDefender, const CvPlot* pPlot, int iOurStrength, int iOurFirepower, int& iTheirOdds, int& iTheirStrength, int& iOurDamage, int& iTheirDamage, CombatDetails* pTheirDetails) const
    {
    	iTheirStrength = kDefender.currCombatStr(pPlot, this, pTheirDetails);
    	int iTheirFirepower = kDefender.currFirepower(pPlot, this);
    ...
    
    As we see, the same takes place for the defender which actually is not that much surprising, is it?

    In total, maxCombatStr now has been called six times now, thrice for the attacker, thrice for the defender.
    In any way, if the respective units are not at 100% of their hitpoints, the results for currCombatStr and currFirepower are not what the unbiased player would assume them to be.

    Well, so far so bad.
    But it can get even worse. :)

    currCombatStr(...) is called by a function
    Code:
    int CvUnit::currEffectiveStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
    {
    	int currStr = [B]currCombatStr[/B](pPlot, pAttacker, pCombatDetails);
    
    	currStr *= (maxHitPoints() + currHitPoints());
    	currStr /= (2 * maxHitPoints());
    
    	return currStr;
    }
    
    Here we are calling the maxCombatStr(...) via currCombatStr(...) again, but now we are changing the result as displayed. :)
    I really don't get it.

    Well, actually I assume that Firaxis desperately tried to somehow "heal" the changed values and actually, the result of this function is closer to what we would have expected, but nevertheless, it is still wrong.
    But at least, we have done some additional calculations... modern cpus like it warm and cosy. :)

    Luckily, this function is only called by
    CvUnit* CvSelectionGroupAI::AI_ejectBestDefender(...)
    int CvPlayerAI::AI_getEnemyPlotStrength(...)
    int CvPlayerAI::AI_getOurPlotStrength(...)
    int CvUnitAI::AI_sacrificeValue(...) const
    int CvPlayerAI::AI_sumAttackerStrength(...)
    int CvSelectionGroupAI::AI_sumStrength(...) const
    int CvPlot::AI_sumStrength(...) const
    of which some are looping through all units on the respective plot, adding up the values.

    And finally, we have these two functions:
    Code:
    float CvUnit::maxCombatStrFloat(const CvPlot* pPlot, const CvUnit* pAttacker) const
    {
    	return (((float)(maxCombatStr(pPlot, pAttacker))) / 100.0f);
    }
    
    and
    Code:
    float CvUnit::currCombatStrFloat(const CvPlot* pPlot, const CvUnit* pAttacker) const
    {
    	return (((float)(currCombatStr(pPlot, pAttacker))) / 100.0f);
    }
    
    which are called from here
    Code:
    float CyUnit::maxCombatStrFloat(CyPlot* pPlot, CyUnit* pAttacker)
    {
    	return m_pUnit ? m_pUnit->maxCombatStrFloat(pPlot->getPlot(), pAttacker->getUnit()) : 0.0f;
    }
    
    and here
    Code:
    float CyUnit::currCombatStrFloat(CyPlot* pPlot, CyUnit* pAttacker)
    {
    	return m_pUnit ? m_pUnit->currCombatStrFloat(pPlot->getPlot(), pAttacker->getUnit()) : 0.0f;
    }
    
    And I am pretty sure that I haven't found all places yet. :)


    In short, the message is that it may take longer than expected to make all these things at least a bit more meaningful.
     
  12. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,355
    I'm not concerned with performance here. It isn't a performance hotspot. However faster is always better.

    What I do care about is calculations, which makes sense. I looked at the code at one point and decided not to touch it until I had a really good reason to mess with it. Considering what you have written, I would be inclined to say that it might make sense to try to scrap the code and start over with a completely new system. The current system appears to be beyond broken.

    I wonder if it would make sense to make a system where each round goes as follows:
    We have two units called A and D.
    Both A and D rolls an attack and a defense based on current health and stuff.
    Attack and defense rolls are compared to tell if the unit is hit. That is done for both ways in this step (read: before damage)
    Critical hits can counter being hit by the other unit even if it hit you.
    Damage is calculated for units being hit (both ways)
    The side, which had the lowest hit score (hit, not damage) will only provide half damage. Combat animation will show the other one as attacking (shooting or whatever)
    Damage is applies
    End combat checks
    next round


    One important thing here is that units in combat are less likely to survived unhurt. It is unrealistic to only deal damage to one side each round as a unit winning 3 out of 4 rounds (statistically) will be unharmed after 5 rounds in around 1 out of 4 combats. If it winds 4 out of 5 rounds, it will be unharmed in 1 out of 3 combats (at 5 rounds). Also the vanilla combat system leaves a door open for the really annoying issue in civ1 where you risked losing strong units like battleships against a warrior or some other unit with 1 in defense. While outcome shouldn't be too predictable, the current system allows the extreme outputs to happen fairly often.
     
  13. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    It is. :)

    Another thing is the way in which combat modifiers are allocated to which unit. :)
    In short:
    Veteran promotions and combat modifiers by traits (nations or FF) are allocated on the respective side.
    All other modifiers are allocated on the side of the defender: defender's values are added, attacker's values are subtracted.
    As this can lead to -100% on the side of the defender, we differentiate between two cases (modifier > 0 and modifier < 0) which are split in two different ways of allocating the total modifier. :)

    Actually you are thinking pretty much along the same lines as I do, except that I wouldn't have a "defensive roll" but give both units a "combat roll" in the same internal combat round. Both may hit or not, so per internal round there are four possible cases:
    1) A+ D+
    2) A+ D-
    3) A- D+
    4) A- D-

    In the case of one side not hitting, half of the normal damage would be allocated to the opponent.

    Damage allocation would take place just the normal way (and even there we may make things easier, as for damage allocation currFirepower and a StrengthFactor are taken into consideration, again).
     
  14. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    Just thinking loudly....

    As I pointed out above already:
    This leads to the fact that combat values are not as the player expects them to be.

    An example (all values here are just examples):
    Say we're having an attacker with base combat strength 10 and woodsman1 (10%)
    Say we're having a defender with base combat strength 8 and woodsman2 (20%)
    The combat always takes place on the plot of the defender, which in this case shall be a grassland tile with a forest (25% defense). It is no hill.

    One would expect the values being:
    A = (base combat strenght) 10 + (10% via woodsman) 1 = 11
    D = (base combat strenght) 8 + (20% via woodsman) 1.6 + (25% via forest) 2 = 11.6
    pA (to hit) = 48.7%

    In fact, it is just
    A = 10
    D = 8 + 0.8 (the attacker modifier is deducted from the defender modifier) + 2 = 10.8
    pA (to hit) = 48.1%

    The difference in this example doesn't look that big, but since we don't calculate just one shot but the whole fight and we can have all kind of different modifiers on both sides, there may be differences up to 5%.
    In combination with how damage is calculated and allocated, this leads to completely different combat odds than one would expect.

    I am intending to create a new structure CombatDetails (there is already one in vanilla) in which I want to store the different modifiers independently for both sides.
    My intention is to have a defined place from where after the calculation of strengths and odds later any other function can pick the needed values.

    I am planning to do this since in the meantime I've found out that even the CvGameTextMgr.cpp makes use of these values.
    In there, it retrieves certain values via expressions like this:
    iModifier = pAttacker->unitClassAttackModifier(pDefender->getUnitClassType());

    This unitClassAttackModifier should have been filled in the structure already and I assume that access to the structure will be quicker than as shown above.
    Is this assumption correct? Nightingale or any other who knows this?
     
  15. vetiarvind

    vetiarvind Prince

    Joined:
    Oct 20, 2013
    Messages:
    328
    Location:
    Chennai, South India
    Not trying to deliberately play devil's advocate, but I honestly think the code makes sense. They are trying to average out current and max power - to use an analogy from classic Civilization - a pikeman vs tank battle. A damaged tank ought to be able to do reasonably well against a full-strength pikeman. Having a firepower variable allows them to do this scenario. Also, they're doing the same for current and max hitpoints. One thing that doesn't make sense to me is why the currentEffectiveStrength makes use of hitpoints whereas the resolveCombat() values are using the current and max strength. Why are they squaring the calculated strength with hitpoints?

    So the resulting "strength" = (avg of currentstrength, maxstrength) * (1+ currentHealth/maxHealth)

    I wouldn't say it's a laughable calculation. Just my opinion. ;)
     
  16. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    Well, the point is that the whole setting is based on just two components:
    strength (as defined per unit/profession times any modifiers) and health (the remaining hitpoints), resulting in "currStrength".
    By the wording, "firepower" for instance, one might assume that they once had something different in mind, but that obviously never made it into the game.

    I for my person don't see any reason, why the given "strength" should be adjusted to something different now, especially not if we look at the words: max, current and effective...
    It just costs calculation time and makes the combat results unpredictable for the human player (if not looking at the combat odds popup).

    Honestly, I think they somewhen have completely lost track of what they originally wanted to achieve.

    What I want to achieve, though, is to have combat results which are according to what the player expects if looking at the units. And best, if we can achieve this with as few calculations as possible.
    The RNG will do the rest, we don't need any obscure and hidden functions to bring in even more surprises.
     
  17. vetiarvind

    vetiarvind Prince

    Joined:
    Oct 20, 2013
    Messages:
    328
    Location:
    Chennai, South India
    I agree that the way they have done the concept of "firepower" is flawed. Combats could very well just be a slugfest between current strengths alone. Althought like nightinggale, I would really not care about the performance aspect at all. You could make the formula 10,000 times more recursive and the formula involved and it would still be absolutely fine. From a human understanding perspective - i agree that the current system is a bit weird.
     
  18. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    Although you are generally right, I have a different view at it.

    First of all, "good" (or economical) programming is achieving the design goal with as few calculations needed as possible - at least in my view.

    Second, I have a vision of how the game could once become.
    For instance, I would like to have units on the march notify enemies around - what they currently don't do. They may spot the enemy when ending their turn, but not in between.
    To change this, one would have to check the surroundings with each single step made. And that will cost quite some computing power.
    So, any savings made now may lower the total burden later.

    But I digress....

    What is most important for me at the moment - does anyone have an idea about my question from above, whether temporarily storing combat values in a structure will be more efficient than always have to pick them like here?
    Code:
    iModifier = pAttacker->unitClassAttackModifier(pDefender->getUnitClassType());
     
  19. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,355
    All this talk about performance is no good. We need to figure out how to calculate what we want and then consider performance. The amount of combat is fairly low compared to the overall picture and I think it's safe to assume most of us have more powerful computers than when colo was originally released, hence more CPU power to calculate combat.

    There is a cache array in each plot telling if the plot is visible. I never fully understood why this isn't used more.

    When moving a unit, the check would be something like:
    - should announce to local player
    - is visible to local player

    First check is fixed based on war status and settings and can actually be cached fairly easy. Second check is looking up the cache in the plot.

    There are some special cases such as pirates and rangers, but overall check when moving into plot shouldn't be hard to display in the GUI.

    I think rather than performance concerns, it could make sense to make a class for combat purely to organize the code better. In fact make a combat class where the constructor takes two units. Write that class in a new cpp/h fileset and we will have a clear view of where in the code the combat takes place.

    We can then make member functions like doRound() and stuff to make clean and readable code.

    I guess performance could improve by such a clean implementation because it will likely be easier to implement caches to prevent calculating numbers like power multiple times.
     
  20. Commander Bello

    Commander Bello Say No 2 Net Validations

    Joined:
    Sep 3, 2003
    Messages:
    3,858
    Location:
    near Koblenz, Germany
    To this I completely agree. :)

    Duly noted. :)

    Please don't forget that you're talking to me. :eek:
    I do have a vague vision of what you're talking about, but that's it.
    Making classes, creating constructors, having new cpp/h files.... way beyond my skills.

    I would be more than happy if you were willing to guide me through that process, as it would help me and increase my capabilities, thus giving me the chance to contribute quicker, better and more efficiently to the whole modding process - but at the moment I have to admit that I can't do what you're proposing. :blush:
     

Share This Page