Dungeon Adventure MOD MOD

Nice work. Once I finish my Advance Wars mod I will continue work on my civrpg mod and I might use some of your work in that once I start working on it again.

My game will be mostly outdoors, but I plan to add a map switching feature to my mod so I could add dungeons which you can enter etc. Not promising anything about that, but I need that feature in my campaign mode for AdvWars so I'll know before I start working on civrpg again if I can include dungeons.

My mod have no civilization or city management, just character development with all that usually comes with digital RPGs. Inventory screen, equipment, attributes, quests, monsters and heroes.

I just wished I had a connection at home so I could put together a team once I get to content development. I will need writers (for storymode), artists (3D and 2d) and designers (idea-comer-up'ers), and possibly programmers (but I'll do most of that myself, as well as the above jobs).

Atm I have Jono and Chandrasekhar on the team, but the project has been dormant for a while mostly cause I have no internet connection.

Anyways, I'll be following this project.
 
Could you not just make it cast pillar of flame instead of ring of flames to get it to strike its own tile? Then it should work.

The pillar of fire spell has Python code that prevents the casting unit from hitting itself or other friendlies. I haven't figured out a way to short-circuit this without causing other problems, such as:

1. Very laggy python code.
2. Traps deciding to blow themselves up for no reason.
3. Traps blowing up other barb units.

Here is the Pillar of Fire def req python below, which I have repurposed for traps:

def reqActivateTrap(caster):
iX = caster.getX()
iY = caster.getY()
pPlayer = gc.getPlayer(caster.getOwner())
iTeam = pPlayer.getTeam()
eTeam = gc.getTeam(iTeam)
for iiX in range(iX-1, iX+2, 1):
for iiY in range(iY-1, iY+2, 1):
pPlot = CyMap().plot(iiX,iiY)
bEnemy = false
bNeutral = false
for i in range(pPlot.getNumUnits()):
pUnit = pPlot.getUnit(i)
if eTeam.isAtWar(pUnit.getTeam()):
bEnemy = true
else:
bNeutral = true
if (bEnemy and bNeutral == false):
return true
return false

I cannot for the life of me understand how this is working. It does prevent the spell from being castable until there is an enemy unit within range that does not share a tile with friendlies. So, it DOES work, but the code contains too many double negatives for me to keep track of the logic. I tried to parse it again last night, but that last condition with the bEnemy and bNeutral throws me off.

If someone could explain to me exactly what this is doing and HOW, I could probably figure out a way to change traps to activate on their own tile only.

Here was my attempt for a new def req (it is commented out in the example below):

#def reqActivateNewTrap(caster):
# pPlayer = gc.getPlayer(caster.getOwner())
# iTeam = pPlayer.getTeam()
# eTeam = gc.getTeam(iTeam)
# pPlot = caster.plot()
# for i in range(pPlot.getNumUnits()):
# pUnit = pPlot.getUnit(i)
# if pUnit.getTeam() != eTeam:
# return true
# return false

All this accomplished was to make the game very laggy and make traps not work at all. I thought it would simply detect if an enemy unit was on the same plot as the trap unit, and not care if friendlies or neutrals were also present. Apparently not!

Again, commentary and observations on the code examples above are welcomed!
 
if bEnemy is practically short for "if bEnemy == true", it's not "if bEnemy AND bEnemy both False"

The statement could have been written like this as well:
if ((bEnemy) and (not bNeutral)):
 
And a few minutes after I made that last post, i looked at the code again and though... "Huh. I wonder if THIS would work."

IT DID! :D

I now have traps that will trigger if an enemy steps on the tile, regardless of whether friendlies are also present. And the traps do not trigger prematurely, or if friendlies only enter the tile. And they only apply the damage effect to units on their own single tile.

By using this code and the code I was using before, I now have two flavors of traps: single tile trigger and effect traps -- and area trigger and effect traps.

Dungeon Adventure just got a little more interesting!
 
Number 1 Reccomendation: use [ code ] [ /code ] when you post code :P It is hard to read quickly without the indentations ;)

My take on the code, could easily be wrong/missing something.

def reqActivateTrap(caster):
iX = caster.getX()
iY = caster.getY()


This part stores the tile that the caster is located on


pPlayer = gc.getPlayer(caster.getOwner())
iTeam = pPlayer.getTeam()


This establishes which player cast the spell and who is on his team.

eTeam = gc.getTeam(iTeam)

This establishes who all the other players are in the game.

for iiX in range(iX-1, iX+2, 1):
for iiY in range(iY-1, iY+2, 1):


This says that it is going to look at all the tiles in the 3x3 grid centered on the caster.

pPlot = CyMap().plot(iiX,iiY)
bEnemy = false
bNeutral = false


This initializes the variables to state that, should nothing further be done with the code, the computer should assume there are no enemies and no neutral units on the tile it is currently checking.

for i in range(pPlot.getNumUnits()):

This sets up to check each unit on the plot, 1 by 1.

pUnit = pPlot.getUnit(i)
if eTeam.isAtWar(pUnit.getTeam()):
bEnemy = true
else:
bNeutral = true



This says that if one of those units belongs to a player who is at war with you, then it is an enemy. But if it belongs to a player who is NOT at war with you, then it is Neutral.

if (bEnemy and bNeutral == false):
return true


This will return a true value if none of the units on the tile were friends or enemies. Which is impossible, thus it returns the value of true if the tile was empty, or only contained your own teams units.

return false

And if the last check didn't work (ie - there is any unit not belonging to your team in that 3x3 grid), this one returns a false.



As for the code you have #'d out, the problem I see with it is the line:

# if pUnit.getTeam() != eTeam:

You are checking only the tile that the caster himself is on, and you are checking for EVERY unit, to include the caster. Thus the caster himself will always trigger this one to return a True.
 
I'd like to use the ranged combat eventually, but I do not think it will be in the first version. I have alot of xml and python to untangle before I can implement it. I am also trying to figure out targeted spells -- didn't FfH2 used to have these?

Anyway - I agree that ranged combat would be great for the Thiefy types, but it need to wait.

Ranged attacks are actually very simple to implement, You just need to give a non-air unit air combat and air range values. the damage is dependant on the air combat and collateral damage based on it's regular collateral damage numbers. it's basically the same as bombing missions but it uses a different button, can't be intercepted, and is blocked by some terrains.
 
Number 1 Reccomendation: use [ code ] [ /code ] when you post code :P It is hard to read quickly without the indentations ;)

Noted! Do the indents make any difference in how the game engine interprets the code? I thought they were just for ease of human reading, but if I'm wrong on that, that could explain a lot of my errors.

pUnit = pPlot.getUnit(i)
if eTeam.isAtWar(pUnit.getTeam()):
bEnemy = true
else:
bNeutral = true



This says that if one of those units belongs to a player who is at war with you, then it is an enemy. But if it belongs to a player who is NOT at war with you, then it is Neutral.

So it doesn't try to process the caster's own units?


if (bEnemy and bNeutral == false):
return true


This will return a true value if none of the units on the tile were friends or enemies. Which is impossible, thus it returns the value of true if the tile was empty, or only contained your own teams units.

Wait, doesn't return true mean that the spell can be cast? Under this interpretation, the spell could only be cast if a nearby tile is either totally empty or full of only your own team's units. This is where I'm getting tripped up. Because in-game, the spell can only be cast if there is a tile in range that contains only enemy units.
 
In any event, I now have single tile traps working fine, except that the "trap sprung" interface message never appears. The unit damaged message does appear, though. I think it is because the python is trying to put two messages in the same space, so it just does the first message.

Area traps work perfectly -- both the damage message and the "trap sprung" message appear. The damage message appears on the activating unit, the "trap sprung" message appears on the trap tile.

I am also implement a special type of trap called a "glyph." These traps cast spells upon activation. Great for summons, and blinding light, etc. For some reason, the "trap sprung" message never appears for these either, despite the fact that they, too, are a form of area trap. The message associated with the spell cast does show up, though.

Very puzzly!
 
Read my post again Lutefisk ;)

if bEnemy is practically short for "if bEnemy == true", it's not "if bEnemy AND bEnemy both False"

The statement could have been written like this as well:
if ((bEnemy) and (not bNeutral)):
 
Ah I see, the == only applies on the second one, so there was an implied == True on the bEnemy tag. Thus it returns the True value if there is an enemy, but no Neutrals :) Cute :p

I had realized that a True seemed just flat out wrong for my case, but assumed it may have been a "Cannot cast" entry, since you find those sometimes in the python as well.

Not sure if the Python relies upon the tabs to be honest.
 
Python relies very heavily on tabs. In python, everything in a function, conditional, loop, etc., must be indented once more than the opening statement. For example,
Code:
 def reqActivateTrap(caster):
iX = caster.getX()
iY = caster.getY()
pPlayer = gc.getPlayer(caster.getOwner())
iTeam = pPlayer.getTeam()
eTeam = gc.getTeam(iTeam)
for iiX in range(iX-1, iX+2, 1):
for iiY in range(iY-1, iY+2, 1):
pPlot = CyMap().plot(iiX,iiY)
bEnemy = false
bNeutral = false
for i in range(pPlot.getNumUnits()):
pUnit = pPlot.getUnit(i)
if eTeam.isAtWar(pUnit.getTeam()):
bEnemy = true
else:
bNeutral = true
if (bEnemy and bNeutral == false):
return true
return false
is quite wrong. First, the define itself must be indented once for it to be a part of class (whatever file it is in). (Edit: I just noticed that in the spells file defines are not indented, unlike in the custom functions, event manager, ect. So you probably should unindent the "right" code once.) Then, everything in it must be indented to be part of the define. Likewise things in the for loops must be indented if they are to be included in the portion of the code to be iterated. "If" statements do the portion of the code below that is indented, and then do unindented lines anyway. The same goes for "else," which I'm pretty sure is meaningless unless it comes immediately after an if (or elif) statement ends. Almost everything in this code would cause a serious error if not properly indented.

The right way is:
Code:
	def reqActivateTrap(caster):
		iX = caster.getX()
		iY = caster.getY()
		pPlayer = gc.getPlayer(caster.getOwner())
		iTeam = pPlayer.getTeam()
		eTeam = gc.getTeam(iTeam)
		for iiX in range(iX-1, iX+2, 1):
			for iiY in range(iY-1, iY+2, 1):
				pPlot = CyMap().plot(iiX,iiY)
				bEnemy = false
				bNeutral = false
				for i in range(pPlot.getNumUnits()):
					pUnit = pPlot.getUnit(i)
					if eTeam.isAtWar(pUnit.getTeam()):
						bEnemy = true
					else:
						bNeutral = true
					if (bEnemy and bNeutral == false):
						return true
		return false



(No, spaces won't work, and telling them apart can get really annoying sometimes. Also, having a space before a tab can sometimes throw things off. What is worse is that IDLE's proposed solution to this is to change every tab into spaces and make you go through every line yourself. You should really edit it in notepad, notepad2, notepad++, etc, which allow you to show the spaces and tabs.)



If this was C++ then the tabs would be irrelevant (only there to make it easier for us to read), because it relies on {} instead. I believe that line endings are also pretty meaningless here.

The Same is true of XML (except for the {} would be more like </>, and it isn't really a programming language)
 
:
Read my post again Lutefisk ;)

The light finally went on. I think I get it now.

The implicit logic to that line of code is:

if (bEnemy == true) and if (bNeutral == false):

Right?

I was thinking it was:

if (bEnemy == false) and if (bNeutral == false):

which was why I was so confused. I realize that the code is nice and consise, but in this instance it could have been written more explicitly. I am a big fan of verbose code for mods -- it makes it easier for noobs like me to figure out what the code is doing!

Thanks to you for squaring me away!
 
The implicit logic to that line of code is:

if (bEnemy == true) and if (bNeutral == false):

Right?
Yup. If you're used to conflating 1 with true and 0 with false it's a little clearer - those binary flags are tricksome beasties! :D
 
Python relies very heavily on tabs. In python, everything in a function, conditional, loop, etc., must be indented once more than the opening statement.

Oh!!!

This is a VERY good thing to know! The only other significant programming I have done was in a type of JavaScript, in which indents are cosmetic only. I also programmed in Basic and APL, but that was back in the day of GOTO and GOSUB.

Thank you for straightening me out, MagisterCultuum. This should reduce my coding frustration significantly.

FWIW, the code I posted got left-justified because I forgot to use the CODE tags. It had tabbed indents, but now I need to go back and examine them to see if they made sense.
 
So here is the def reqcode I am currently using for single tile traps:

Code:
def reqActivateTrap(caster):
	iX = caster.getX()
	iY = caster.getY()
	pPlayer = gc.getPlayer(caster.getOwner())
	iTeam = pPlayer.getTeam()
	eTeam = gc.getTeam(iTeam)
	for iiX in range(iX-0, iX+1, 1):
		for iiY in range(iY-0, iY+1, 1):
			pPlot = CyMap().plot(iiX,iiY)
			bEnemy = false
			bNeutral = false
			for i in range(pPlot.getNumUnits()):
				pUnit = pPlot.getUnit(i)
				if eTeam.isAtWar(pUnit.getTeam()):
					bEnemy = true
#				else:
#					bNeutral = true
#			if (bEnemy and bNeutral == false):
			if (bEnemy == true):
				return true
	return false

If I understand the previous discussion correctly, this version should work, too:

Code:
def reqActivateTrap(caster):
	pPlot = caster.plot()
	pPlayer = gc.getPlayer(caster.getOwner())
	iTeam = pPlayer.getTeam()
	eTeam = gc.getTeam(iTeam)
	bEnemy = false
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if eTeam.isAtWar(pUnit.getTeam()):
				bEnemy = true
		if (bEnemy == true):
			return true
	return false

The purpose of the above code is to make the spell castable only if an enemy is on the same tile as the caster, regardless of the presence of friendly or neutral units.

Comments?
 
:

I am a big fan of verbose code for mods -- it makes it easier for noobs like me to figure out what the code is doing!

Yeah I am a fan of verbose code as well and python seems to have been designed to basically work as a pseudo-code like programming language.

Instead of && it has and , instead of ! it has not,etc

Even the For loop has been made to be more understandable. Instead of C's for(i=0,i < x,i++) it has the for x in range(range), which even makes a sentence when read. If the programmer is aware of this and put some effort down you can write very easy read code.

An example:
Code:
[COLOR=Green]#Example check function[/COLOR]
[B][COLOR=Blue]def[/COLOR][/B] isNotAI(iPlayer):
   [COLOR=PaleTurquoise] [COLOR=Yellow] [COLOR=SeaGreen]'Returns true if iPlayer is human'[/COLOR][/COLOR][/COLOR]
     pPlayer = gc.getPlayer(iPlayer)
     [COLOR=Blue][B]if[/B][/COLOR] pPlayer.isHuman():
          [B][COLOR=Blue]return[/COLOR][/B] true
     [B][COLOR=Blue]return[/COLOR][/B] false

[COLOR=Green]#Then our actual code
[COLOR=Black]bDone = false[/COLOR]
[COLOR=Black][B][COLOR=Blue]while [/COLOR][COLOR=Blue]not[/COLOR][/B] bDone:[/COLOR]
[/COLOR][COLOR=Blue][B]     for[/B][/COLOR] iPlayer [B][COLOR=Blue]in[/COLOR][/B] gc.getNumPlayers():
         [B][COLOR=Blue]if[/COLOR][/B] isNotAI(iPlayer):
               #Do Something
               bDone = True
 
In the first version the second to last if statement won't do anything regardless.

If you don't care about friendly or neutral units, why not just get rid o the bEnemy variable and just put the return true statement in "if eTeam.isAtWar(pUnit.getTeam()):" instead? The code would be faster that way, as it would stop checking the units on the tile once it finds a single enemy.
 
In the first version the second to last if statement won't do anything regardless.

You mean the lines that are commented out? Yeah, I know... I leave bread crumbs for myself like that when I'm fiddling with code. It helps me revert to earlier versions when I screw things up.

If you don't care about friendly or neutral units, why not just get rid o the bEnemy variable and just put the return true statement in "if eTeam.isAtWar(pUnit.getTeam()):" instead? The code would be faster that way, as it would stop checking the units on the tile once it finds a single enemy.

So, like this:

Code:
def reqActivateTrap(caster):
	pPlot = caster.plot()
	pPlayer = gc.getPlayer(caster.getOwner())
	iTeam = pPlayer.getTeam()
	eTeam = gc.getTeam(iTeam)
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if eTeam.isAtWar(pUnit.getTeam()):
				return true		
	return false

I like it, nice and crisp.
 
The For loop (starting with the for statement and including everything in it) is indented once too much.
 
ive got a question:

I need help with a certain spell that i want to make for the Warhammer mod, which uses ffh code for spells. after reading about these traps that lutefisk is making, i had a sudden brain wave. is it at all possible to d othe following for a spell:

Comet of Cassandora:
this spell shoud summon a permanent, invisible, single tile trap unit which is allied to the caster.
the single tile trap, should have a % chance of being 'set off' each turn, regardless of if there is an enemy on it, and if an enemy steps on it, it should be set off immediately regardless. it damages all units, friend and foe.
then it is set off, either by the % chance or being stepped on, it should cast a direct damage spell that does a small ammount of damage.
however, every turn that the comet trap exists, the Damage level should increase, and every 2 turns the area of effect should increase by one square, until after 6 turns the trap is automatically set off (ie chance of setting off change to 100%).

numerically i would like it to be like this:
turn 1: 15% fire damage to any unit directly on top of the comet trap. 10% chance of setting off.
turn 2: 30% fire damage to any unit directly on top of, or adjacent to the comet trap. 20% chance of setting off.
turn 3: 45% fire damage to any unit directly on top of, or adjacent to the comet trap. 40% chance of setting off.
turn 4: 60% fire damage to any unit directly on top of, or within 2 squares of the comet trap. 60% chance of setting off.
turn 5: 75% fire damage to any unit directly on top of, or within 2 squares of the comet trap. 80% chance of setting off.
turn 6: 90% fire damage to any unit directly on top of, or within 2 squares of the comet trap. 100% chance of setting off.

my question then is, how would i make the comet trap change the % chance, ammount of damage, and spell area each turn? is it even possible?

if yes, could someone please provide me with the code that should make it work? and where should i put the code? im almost entirely python illiterate:p

sorry for hijacking this thread Lutefisk :p

EDIT: i realise im asking a lot here, but one can always hope ay? ;)
EDIT 2: lutefisk, how does your trap promotion work?
 
Back
Top Bottom