Jump to content

Concordance of new features


Ascension64

Recommended Posts

Backtracking a bit, I'd be asking a few questions:

 

Is there any opcode or equivalent that does a RestrorePartyLocation()?

 

Opcode 124 has got this IWD setting that when param2 = 2, then it restores the .cre's location from a stored value. What stores this value, how can it be done? Does it store area reference or just x,y coordinates?

 

Is it possible in summoning opcodes to dictate the time the .cre's avatar/foot circle is not drawn?

 

-Galactygon

Link to comment
I found no opcode for this.

Does it involve a lot of effort to make one?

 

Is it possible in summoning opcodes to dictate the time the .cre's avatar/foot circle is not drawn?

We haven't heard from you about this.

 

Let's discuss opcode #239 [Farsight]. From FXOpcodes.cpp under gemrb/plugins/FXOpcodes.cpp:

// 0xef Farsee

// 1 view not explored sections too

// 2 param1=range (otherwise visualrange)

// 4 point already set (otherwise use gui)

// 8 use line of sight

Am I right to assume param1 is treated as bits rather than as a single value? I can guess what bit 1 (param1=range) does, and maybe what bit 2 does - it reveals the point around which it is cast, so you could set the extended header target type to area and not have the player go through the GuI - a nice change. But I couldn't guess what bit 3 is supposed to do.

 

There is a bug in the original game, and I'd convince Ascension64 to fix it: unconscious characters are invisible to Farsight, but nondetected characters aren't.

 

-Galactygon

Link to comment

Restoring party locations: no, it is not even an effort since we got the internal functions.

I just see no point, as no one would use it. (After ToBEX has an implementation of it and a single mod uses it, it will have a bigger chance).

 

summoning feet circle: until yesterday i didn't even know how this is done in the original engine. Now i see there are hardcoded timings for a selected few animations. I would support the timing as param1 or 3. If it is 0, the default is hardcoded in tob (we will take the adjusted time of the vvc in gemrb, as we loathe hardcoding).

 

farsee: the bitfield is param2.

Use line of sight means, the 'farsee' is not from above the area (and through roofs, etc), but like the caster would stay in the epicentre. So, line of sight applies.

 

That farsee bug is definitely something we will not copy.

 

By the way, i might alter how the explore flag works (normally it is just a 0/nonzero), but i would use value 2 as 'see through walls'. I don't know how farsee is showing the area as visible for some time (we don't).

 

I found no opcode for this.

Does it involve a lot of effort to make one?

 

Is it possible in summoning opcodes to dictate the time the .cre's avatar/foot circle is not drawn?

We haven't heard from you about this.

 

Let's discuss opcode #239 [Farsight]. From FXOpcodes.cpp under gemrb/plugins/FXOpcodes.cpp:

// 0xef Farsee

// 1 view not explored sections too

// 2 param1=range (otherwise visualrange)

// 4 point already set (otherwise use gui)

// 8 use line of sight

Am I right to assume param1 is treated as bits rather than as a single value? I can guess what bit 1 (param1=range) does, and maybe what bit 2 does - it reveals the point around which it is cast, so you could set the extended header target type to area and not have the player go through the GuI - a nice change. But I couldn't guess what bit 3 is supposed to do.

 

There is a bug in the original game, and I'd convince Ascension64 to fix it: unconscious characters are invisible to Farsight, but nondetected characters aren't.

 

-Galactygon

Link to comment
Maybe it's a summoning effect. :undecided:

Huh?

 

Ascension64, Galactygon: gemrb treats opcode 12d (critical hit modifier) as standard stat modifier.

So, you can have a critical hit bonus as well as a flat setting. (original has only flat setting).

 

Also the same for npc bump and no tracking (though i don't really know if these have any use).

Link to comment

I intend to implement into TobEx beta 0012 a fix for the stacking issues with 0x001 Attacks Per Round Modifier. The issues were specified at http://forums.pocketplane.net/index.php/to....html#msg322006, and Taimon attempted a fix in tob_hacks.

 

The implementation is a conversion of key->float, calculation using float, and finally storage via float->key. Two function changes were required, the effect opcode ApplyEffect() function, and the CDerivedStats::operator+() function. Here is the current description as per TobEx_ini.txt.

 

-----Attacks Per Round Mod Fix

Fixes the attacks per round modifier opcode to integrate the 1-5 and 1.5-4.5 attacks per round

Different implementation to tob_hacks, invalidates tob_hacks

 

Description

#1 (0x001) Stat: Attacks Per Round Modifier [1]

Parameter #1: Value

Parameter #2: Type

Description:

Alters a characters Attacks per Round in the style specified by 'Type' field.

 

Key Attacks Per Round

0 0

1 1

2 2

3 3

4 4

5 5

6 0.5

7 1.5

8 2.5

9 3.5

10 4.5

 

Known values for 'Type' are:

0 Cumulative Modifier -> Key = Key + Value [Value is of type 'Key' with support for negative keys]

1 Flat Value Modifier -> Key = Value [Value is of type 'Key']

2 Percentage Modifier -> Key = Key * value / 100 [Value is of type 'positive integer', with rounding to the nearest 0.5 attacks]

 

NB. Unlike the original engine implementation, stacking this opcode stacks the effect on the number of attacks, not the key.

 

The keys are kept for backwards compatibility, but we know that integer values and the half values do not mix in the original engine implementation. Here is the original code:

 

	switch (effect.nParam2) {
case 0: //add
	if (effect.nTiming == 1) {
		if (creTarget.m_BaseStats.numAttacks <= 5) {
			creTarget.m_BaseStats.numAttacks += creTarget.m_BaseStats.numAttacks + effect.nParam1;
			if (creTarget.m_BaseStats.numAttacks > 5) creTarget.m_BaseStats.numAttacks = 5;
			if (creTarget.m_BaseStats.numAttacks < 0) creTarget.m_BaseStats.numAttacks = 0;
		} else {
			creTarget.m_BaseStats.numAttacks += creTarget.m_BaseStats.numAttacks + effect.nParam1;
			if (creTarget.m_BaseStats.numAttacks > 10) creTarget.m_BaseStats.numAttacks = 10;
			if (creTarget.m_BaseStats.numAttacks < 6) creTarget.m_BaseStats.numAttacks = 6;
		}

		bRefreshCurrentState = TRUE;
		bPurge = TRUE;
	} else {
		creTarget.cdsDiff.numAttacks += effect.nParam1;
		bPurge = FALSE;
	}
	break;
case 1: //set
	if (effect.nTiming == 1) {
		creTarget.m_BaseStats.numAttacks = effect.nParam1;
		if (creTarget.m_BaseStats.numAttacks > 10) creTarget.m_BaseStats.numAttacks = 10;
		if (creTarget.m_BaseStats.numAttacks < 0) creTarget.m_BaseStats.numAttacks = 0;

		bRefreshCurrentState = TRUE;
		bPurge = TRUE;
	} else {
		if (creTarget.cdsCurrent.numAttacks) {
			creTarget.cdsCurrent.numAttacks = effect.nParam1;
			if (creTarget.cdsCurrent.numAttacks > 10) creTarget.cdsCurrent.numAttacks = 10;
			if (creTarget.cdsCurrent.numAttacks < 0) creTarget.cdsCurrent.numAttacks = 0;
		}
		bPurge = FALSE;
	}
	break;
case 2: //percent
	if (effect.nTiming == 1) {
		if (creTarget.m_BaseStats.numAttacks <= 5) {
			creTarget.m_BaseStats.numAttacks = creTarget.m_BaseStats.numAttacks * effect.nParam1 / 100;
			if (creTarget.m_BaseStats.numAttacks > 5) creTarget.m_BaseStats.numAttacks = 5;
			if (creTarget.m_BaseStats.numAttacks < 0) creTarget.m_BaseStats.numAttacks = 0;
			if (creTarget.m_BaseStats.numAttacks <= 1) creTarget.m_BaseStats.numAttacks = 1;
		} else {
			creTarget.m_BaseStats.numAttacks = (creTarget.m_BaseStats.numAttacks - 5) * effect.nParam1 / 100 + 5;
			if (creTarget.m_BaseStats.numAttacks > 10) creTarget.m_BaseStats.numAttacks = 10;
			if (creTarget.m_BaseStats.numAttacks < 6) creTarget.m_BaseStats.numAttacks = 6;
			if (creTarget.m_BaseStats.numAttacks <= 1) creTarget.m_BaseStats.numAttacks = 1;
		}
		bRefreshCurrentState = TRUE;
		bPurge = TRUE;
	} else {
		if (creTarget.cdsCurrent.numAttacks <= 5) {
			creTarget.cdsCurrent.numAttacks = creTarget.m_BaseStats.numAttacks * effect.nParam1 / 100;
			if (creTarget.cdsCurrent.numAttacks > 5) creTarget.cdsCurrent.numAttacks = 5;
			if (creTarget.cdsCurrent.numAttacks < 0) creTarget.cdsCurrent.numAttacks = 0;
			if (creTarget.cdsCurrent.numAttacks <= 1) creTarget.cdsCurrent.numAttacks = 1;
		} else {
			creTarget.cdsCurrent.numAttacks = (creTarget.m_BaseStats.numAttacks - 5) * effect.nParam1 / 100 + 5;
			if (creTarget.cdsCurrent.numAttacks > 10) creTarget.cdsCurrent.numAttacks = 10;
			if (creTarget.cdsCurrent.numAttacks < 6) creTarget.cdsCurrent.numAttacks = 6;
			if (creTarget.cdsCurrent.numAttacks <= 1) creTarget.cdsCurrent.numAttacks = 1;
		}
		bPurge = FALSE;
	}
	break;
default:
	assert(FALSE);
	break;
}

return TRUE;

 

Does this sound reasonable to implement in both TobEx and GemRB?

Link to comment

We already fixed this issue in GemRB, but not with floats.

We also kept the keys, but we simply use value*2 while we do arithmetics on it.

 

Most likely our code would give the same result, regardless of internal representation, so no need of change on either side.

 

More consideration should be taken if we would allow the decrease of the stat too.

 

And now something else, opcode 0x111:

i'm still amazed how cleanair.2da would work (open it in dltcep if you don't know what i'm talking about).

Anyway, gemrb has several additions to this poorly implemented opcode:

param2

0 - works as original

1 - you can supply your own 2da in the resource field

2 - instead of a list in 2da, you can supply a single projectile number in param1

Link to comment

[qtuoe]More consideration should be taken if we would allow the decrease of the stat too.

This is already done with my float implementation, particularly under the addition/subtration type. Also, the percentage works quite well as well. The rounding behaviour might be different to GemRB's implementation. How does GemRB handle the perentage?

 

And now something else, opcode 0x111:

i'm still amazed how cleanair.2da would work (open it in dltcep if you don't know what i'm talking about).

Anyway, gemrb has several additions to this poorly implemented opcode:

param2

0 - works as original

1 - you can supply your own 2da in the resource field

2 - instead of a list in 2da, you can supply a single projectile number in param1

I see. I haven't looked at that effect, but I'll have a look when I get around to it.

 

 

On another issue. the bool CCreatureObject::TryHit() code hard-codes the critial hit aversion by helmets. Funnily, you can have anything equipped in the helmet slot and it will protect you. I think it would be beneficial to use one of the item flags to disable the critical hit aversion. Perhaps the best way to do this is to two-fold:

1. Restrict critial hit aversion to type helmet

2. Check a new item flag that when set will disable critical hit aversion

Link to comment

The item field is a DWORD, and based on what we know, the high WORD isn't used at all. So I'll probably place the bit at the highest bit31.

 

#define IE_ITEM_CRITICAL	 0x00000001
#define IE_ITEM_TWO_HANDED   0x00000002
#define IE_ITEM_MOVABLE	  0x00000004
#define IE_ITEM_DISPLAYABLE  0x00000008
#define IE_ITEM_CURSED	   0x00000010
#define IE_ITEM_NOT_COPYABLE 0x00000020
#define IE_ITEM_MAGICAL	  0x00000040
#define IE_ITEM_BOW		  0x00000080
#define IE_ITEM_SILVER	   0x00000100
#define IE_ITEM_COLD_IRON	0x00000200
#define IE_ITEM_STOLEN	   0x00000400
#define IE_ITEM_CONVERSABLE  0x00000800
#define IE_ITEM_PULSATING	0x00001000
#define IE_ITEM_UNSELLABLE   ( IE_ITEM_CRITICAL | IE_ITEM_STOLEN )

Link to comment
1. Restrict critial hit aversion to type helmet

2. Check a new item flag that when set will disable critical hit aversion

 

I will pipe in to suggest this flag to work in reverse for items that do not avert critical hits by default.

 

How do these two work?

#define IE_ITEM_NOT_COPYABLE 0x00000020
#define IE_ITEM_PULSATING	0x00001000

 

-Galactygon

Link to comment
I will pipe in to suggest this flag to work in reverse for items that do not avert critical hits by default.

Good idea.

Therefore, anything equipped in SLOT_HELMET would have a flag to turn off aversion of critical hits. All other items would have a flag to turn on aversion of critical hits. The question is: should they use the same bit because they are mutually exclusive (saves bits if we want to use them later), or separate them to prevent modder confusion?

 

Here is some code that uses two separate bits:

BOOL __stdcall CCreatureObject_ShouldAvertCriticalHit(CCreatureObject& creTarget) {
CItem* pItem = NULL;

for (int i = 0; i < 10; i++) {
	pItem = creTarget.m_ItemSlots.items[i];
	if (pItem) {
		if (i != SLOT_HELMET &&
			(pItem->GetFlags() & ITEMFLAG_AVERT_CRITICALHIT)) {
			return TRUE;
		}
		if (i == SLOT_HELMET &&
			!(pItem->GetFlags() & ITEMFLAG_ALLOW_CRITICALHIT)) {
			return TRUE;
		}
	}
}

pItem = creTarget.m_ItemSlots.items[creTarget.m_ItemSlots.weaponSlotSelected];
if (pItem) {
	if (pItem->GetFlags() & ITEMFLAG_AVERT_CRITICALHIT) {
		return TRUE;
	}
}

return FALSE;
}

 

 

How do these two work?

#define IE_ITEM_NOT_COPYABLE 0x00000020
#define IE_ITEM_PULSATING	0x00001000

These are unused in BG2. I think pulsating is a bit used in IWD2.
Link to comment

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...