[FIXED] Bug: Sea creatures can't move in water
-
Grimson
- Developer
- Posts: 802
- Joined: Sat Jun 04, 2005 1:52 am
- Location: Germany
- Has thanked: 0
- Been thanked: 0
Bug: Sea creatures can't move in water
Well after wondering why none of the sea creatures I added spawns for seem to spawn. So I manually placed some of them in the water and they can't move, but they can move on land. So it seems like UOX3 currently doesn't make a difference between sea and land creatures. I'll try to have a look at the code, but that may take some time (depending on how fast I start to understand the movement and mapstuff code).
- Xuri
- Site Admin
- Posts: 3704
- Joined: Mon Jun 02, 2003 9:11 am
- Location: Norway
- Has thanked: 48 times
- Been thanked: 8 times
- Contact:
There's no difference between sea and land creatures at the moment. There was, once upon a time, and there may still be code lingering about from that time, since a complete rewrite of the walking/npc movement code (which included "swimming", and flying) was later modified to fix other bugs - and the support for swimming/flying was removed.
-= Ho Eyo He Hum =-
-
giwo
- Developer
- Posts: 1780
- Joined: Fri Jun 18, 2004 4:17 pm
- Location: California
- Has thanked: 0
- Been thanked: 0
I am still testing Lingo's modifications to mapstuff.cpp, while it doesn't fix this specific problem, it does make the mapstuff files a bit easier to read/use.
I recall that once apon a time there was code supporting fish moving on water in the Walking code. For whatever reason, it was removed ( If my foggy memory serves me, someone rewrote walking alltogether and never added that support back in).
The tiles support it, and it isn't necesarry to actually flag the NPC object telling it whether it can move on water or not, it will just take some time to figure out how to go about it.
I recall that once apon a time there was code supporting fish moving on water in the Walking code. For whatever reason, it was removed ( If my foggy memory serves me, someone rewrote walking alltogether and never added that support back in).
The tiles support it, and it isn't necesarry to actually flag the NPC object telling it whether it can move on water or not, it will just take some time to figure out how to go about it.
Scott
-
Grimson
- Developer
- Posts: 802
- Joined: Sat Jun 04, 2005 1:52 am
- Location: Germany
- Has thanked: 0
- Been thanked: 0
Then I'll wait till it is commited to the CVS.giwo wrote:I am still testing Lingo's modifications to mapstuff.cpp, while it doesn't fix this specific problem, it does make the mapstuff files a bit easier to read/use.
The support to flag something as watercreature is still present in the parsing of the creatures.dfn, so why not use it.giwo wrote:The tiles support it, and it isn't necesarry to actually flag the NPC object telling it whether it can move on water or not, it will just take some time to figure out how to go about it.
-
giwo
- Developer
- Posts: 1780
- Joined: Fri Jun 18, 2004 4:17 pm
- Location: California
- Has thanked: 0
- Been thanked: 0
This is part of the old code for handling Flying/Swimming creatures. As you can see it is incomplete yet, there was something that called MoveHeightAdjustment() but as far back as I can find, it's unrefrenced.
Code: Select all
#define P_C_IS_GM_BODY 0x01 // Bits for different movement types
#define P_C_IS_PLAYER 0x02
#define P_C_IS_BIRD 0x20
#define P_C_IS_NPC 0x40
#define P_C_IS_FISH 0x80
// check if GM Body
bool cMovement::CanGMWalk( CTileUni &xyb )
{
UI16 blockid = xyb.ID();
CTile newTile;
Map->SeekTile( blockid, &newTile );
if( Map->IsRoofOrFloorTile( &newTile ) )
return true;
if( xyb.Type() == 0 ) // map tile
return true;
if( xyb.Blocking() ) // blocking
return true;
if( xyb.Flag2() & 0x16 ) // climbable, standable, windows/doors
return true;
if( xyb.Door() ) // door
return true;
return false;
}
bool cMovement::CanNPCWalk( CTileUni &xyb )
{
UI16 blockid = xyb.ID();
CTile newTile;
Map->SeekTile( blockid, &newTile );
if( Map->IsRoofOrFloorTile( &newTile ) )
return true;
if( xyb.Type() == 0 )
return true;
if( xyb.Standable() || xyb.Climbable() )
return true;
return false;
}
bool cMovement::CanPlayerWalk( CTileUni &xyb )
{
UI16 blockid = xyb.ID();
CTile newTile;
Map->SeekTile( blockid, &newTile );
if( Map->IsRoofOrFloorTile( &newTile ) )
return true;
if( xyb.Type() == 0 ) // map tile
return true;
if( xyb.Standable() || xyb.Climbable() ) // standable, climbable
return true;
return false;
}
bool cMovement::CanFishWalk( CTileUni &xyb )
{
UI16 blockid = xyb.ID();
if( Map->IsTileWet( blockid ) )
return true;
// Can they walk/swim on water tiles?
if( blockid > 0x00A7 && blockid < 0x00AC )
return true;
if( blockid > 0x1796 && blockid < 0x179D )
return true;
if( blockid > 0x346D && blockid < 0x3486 )
return true;
if( blockid > 0x3493 && blockid < 0x34AC )
return true;
if( blockid > 0x34B7 && blockid < 0x34CB )
return true;
// Can they walk/swim on water ripples and splashes?
if( blockid > 0x34D0 && blockid < 0x34D6 )
return true;
if( blockid > 0x352C && blockid < 0x3531 )
return true;
// Can they walk/swim on whirlpools?
// if( blockid > 0x348F && blockid < 0x3494 )
// return true;
// if( blockid > 0x34B4 && blockid < 0x34B8 )
// return true;
// Can they walk/swim on/up waterfalls?
if( blockid > 0x34EC && blockid < 0x3529 )
return true;
// Can they walk/swim on the coastlines?
// if( blockid > 0x179C && blockid < 0x17B3 )
// return true;
// if( blockid == 0x1796 )
// return true;
return false;
}
// needs testing... not totally accurate, but something to hold place.
bool cMovement::CanBirdWalk( CTileUni &xyb )
{
return ( CanNPCWalk( xyb ) || CanFishWalk( xyb ) );
}
bool cMovement::MoveHeightAdjustment( int MoveType, CTileUni *thisblock, int &ontype, SI32 &nItemTop, SI32 &nNewZ )
{
if( ( MoveType & P_C_IS_GM_BODY ) && ( CanGMWalk( *(thisblock) ) ) )
{
nNewZ = nItemTop;
ontype = thisblock->Type();
return true;
}
if( ( MoveType & P_C_IS_PLAYER ) && ( CanPlayerWalk( *(thisblock) ) ) )
{
nNewZ = nItemTop;
ontype = thisblock->Type();
return true;
}
if( ( MoveType & P_C_IS_FISH ) && ( CanFishWalk( *(thisblock) ) ) )
{
nNewZ = nItemTop;
ontype = thisblock->Type();
return true;
}
if( ( MoveType & P_C_IS_NPC ) && ( CanNPCWalk( *(thisblock) ) ) )
{
nNewZ = nItemTop;
ontype = thisblock->Type();
return true;
}
if( ( MoveType & P_C_IS_BIRD ) && ( CanBirdWalk( *(thisblock) ) ) )
{
nNewZ = nItemTop;
ontype = thisblock->Type();
return true;
}
return false;
}
Scott
-
Grimson
- Developer
- Posts: 802
- Joined: Sat Jun 04, 2005 1:52 am
- Location: Germany
- Has thanked: 0
- Been thanked: 0
I think I got it working. You need to make the following changes to movement.cpp:
add this function:
and modify the calc_walk() function this way:
You need to add the "WATERCREATURE" tag to all the creatures, that should be able to walk on water instead of land, inside the creatures.dfn.
But remember, this is still very experimental.
add this function:
Code: Select all
bool cMovement::CanFishWalk( CTileUni *tb )
{
UI16 blockid = tb->ID();
if( tb->LiquidWet() )
return true;
// Can they walk/swim on water tiles?
if( blockid > 0x00A7 && blockid < 0x00AC )
return true;
if( blockid > 0x0135 && blockid < 0x0138 )
return true;
if( blockid > 0x1796 && blockid < 0x179D )
return true;
if( blockid > 0x346D && blockid < 0x3486 )
return true;
if( blockid > 0x3493 && blockid < 0x34AC )
return true;
if( blockid > 0x34B7 && blockid < 0x34CB )
return true;
// Can they walk/swim on water ripples and splashes?
if( blockid > 0x34D0 && blockid < 0x34D6 )
return true;
if( blockid > 0x352C && blockid < 0x3531 )
return true;
// Can they walk/swim on/up waterfalls?
if( blockid > 0x34EC && blockid < 0x3529 )
return true;
return false;
}
Code: Select all
SI08 cMovement::calc_walk( CChar *c, SI16 x, SI16 y, SI16 oldx, SI16 oldy, bool justask )
{
if( !ValidateObject( c ) )
return ILLEGAL_Z;
const SI08 oldz = c->GetZ();
bool may_levitate = c->MayLevitate();
bool on_ladder = false;
SI08 newz = ILLEGAL_Z;
bool blocked = false;
char ontype = 0;
int xycount = 0;
UI08 worldNumber = c->WorldNumber();
bool isSeaCreature = cwmWorldState->creatures[c->GetID()].IsWater();
CTileUni xyblock[XYMAX];
GetBlockingMap( x, y, xyblock, xycount, oldx, oldy, worldNumber );
GetBlockingStatics( x, y, xyblock, xycount, worldNumber );
GetBlockingDynamics( x, y, xyblock, xycount, worldNumber );
// first calculate newZ value
for( int i = 0; i < xycount; ++i )
{
CTileUni *tb = &xyblock[i]; // this is a easy/little tricky, to save a little calculation
// since the [i] is calclated several times below
// if it doesn't help, it doesn't hurt either.
SI08 nItemTop = (SI08)(tb->BaseZ() + ((xyblock[i].Type() == 0) ? xyblock[i].Height() : calcTileHeight( xyblock[i].Height() ) )); // Calculate the items total height
// check if the creature is floating on a static (keeping Z or falling)
if( nItemTop >= newz && nItemTop <= oldz )
{
if ( isSeaCreature )
{
if( CanFishWalk( tb ) )
{ // swimable tile
newz = nItemTop;
ontype = tb->Type();
continue;
}
}
else
{
if( tb->Standable() )
{ // walkable tile
newz = nItemTop;
ontype = tb->Type();
if( tb->ClimbableBit2() ) // if it was ladder the char is allowed to `levitate´ next move
on_ladder = true;
continue;
}
}
}
// So now comes next step, levitation :o)
// you can gain Z to a limited amount if yo uwere climbing on last move on a ladder
if( nItemTop >= newz && may_levitate && nItemTop <= oldz + MAX_Z_LEVITATE && tb->Standable() )
{
ontype = tb->Type();
newz = nItemTop;
if( tb->ClimbableBit2() ) // if it was ladder the char is allowed to `levitate´ next move
on_ladder = true;
}
// check if the creature is climbing on a climbable Z
// (gaining Z through stairs, ladders, etc)
// This form has no height limit, and the tile bottom must start lower or
// equal current height + levitateable limit
if( nItemTop >= newz && tb->BaseZ() <= oldz + MAX_Z_LEVITATE )
{
if( tb->Climbable() || tb->Type() == 0 || // Climbable tile, map tiles are also climbable
( tb->Flag1() == 0 && tb->Flag2() == 0x22 ) || // These are a special kind of tiles where OSI forgot to set the climbable flag
( (nItemTop >= oldz && nItemTop <= oldz + 3) && tb->Standable() ) ) // Allow to climb a height of 1 even if the climbable flag is not set
{
ontype = tb->Type();
newz = nItemTop;
if( tb->ClimbableBit2() ) // if it was ladder the char is allowed to `levitate´ next move
on_ladder = true;
}
}
}
#if DEBUG_WALKING
Console.Print( "DEBUG: CheckWalkable calculate Z=%d\n", newz );
#endif
SI08 item_influence = higher( newz + MAX_ITEM_Z_INFLUENCE, oldz );
// also take care to look on all tiles the creature has fallen through
// (npc's walking on ocean bug)
// now the new Z-cordinate of creature is known,
// check if it hits it's head against something (blocking in other words)
bool isGM = IsGMBody( c );
for( int ii = 0; ii < xycount; ++ii )
{
CTileUni *tb = &xyblock[ii];
SI32 nItemTop = tb->BaseZ() + ( ( tb->Type() == 0) ? tb->Height() : calcTileHeight( tb->Height() ) ); // Calculate the items total height
if( ( ( !isSeaCreature && tb->Blocking() ) || ( isSeaCreature && !CanFishWalk(tb) ) || ( (tb->Standable() || ( isSeaCreature && CanFishWalk( tb ) )) && nItemTop > newz ) ) && // Check for blocking tile or stairs
!( ( isGM || c->IsDead() ) && ( tb->WindowArchDoor() || tb->Door() ) ) ) // ghosts can walk through doors
{
// blocking
if( nItemTop > newz && tb->BaseZ() <= item_influence || ( nItemTop == newz && ontype == 0 ) )
{ // in effact radius?
newz = ILLEGAL_Z;
#if DEBUG_WALKING
Console.Print( "DEBUG: CheckWalkable blocked due to tile=%d at height=%d.\n", xyblock[ii].ID(), xyblock[ii].BaseZ() );
#endif
blocked = true;
break;
}
}
// knoxos: MAX_ITEM_Z_INFLUENCE is nice, but not truely correct,
// since the creature height should be the effect radius, if you are i.e.
// polymorphed to a "slime", you could go through things you normally
// wouldn't get under. (Just leaves the question what happens if you
// unpolymorph in a place where you can't fit, lucky there are no
// such gaps or tunnels in Britannia).
// (Well UO isn't ment to really think in 3d)
}
#if DEBUG_WALKING
Console.Print( "DEBUG: CanCharWalk: %dx %dy %dz\n", x, y, z );
#endif
if( (newz > ILLEGAL_Z) && (!justask) ) // save information if we have climbed on last move.
c->SetLevitate( on_ladder );
return newz;
}
But remember, this is still very experimental.
- Xuri
- Site Admin
- Posts: 3704
- Joined: Mon Jun 02, 2003 9:11 am
- Location: Norway
- Has thanked: 48 times
- Been thanked: 8 times
- Contact:
Looks like it's working Grimson, I've got dolphins and seaserpents swimming on my server right now. Nice work!
As with all experiemental code, it has a few kinks to work out though. For instance, my sea creatures are swimming right through all docks
I.e. they are not blocked by statics if there's a valid water-tile beneath.
Secondly, there is no way to have creatures that can walk on both water and land. For instance Water Elementals. Add WATERCREATURE to creatures.dfn, and they'll be able to swim, but not walk on land. Remove it, and they can walk on land but not on water.
But it's looking good so far
As with all experiemental code, it has a few kinks to work out though. For instance, my sea creatures are swimming right through all docks
Secondly, there is no way to have creatures that can walk on both water and land. For instance Water Elementals. Add WATERCREATURE to creatures.dfn, and they'll be able to swim, but not walk on land. Remove it, and they can walk on land but not on water.
But it's looking good so far
-= Ho Eyo He Hum =-
-
Grimson
- Developer
- Posts: 802
- Joined: Sat Jun 04, 2005 1:52 am
- Location: Germany
- Has thanked: 0
- Been thanked: 0
I'll try to find a solution for this.Xuri wrote: As with all experiemental code, it has a few kinks to work out though. For instance, my sea creatures are swimming right through all docksI.e. they are not blocked by statics if there's a valid water-tile beneath.
So it would be best to add another tag "AMPHIBIAN" for example.Xuri wrote:Secondly, there is no way to have creatures that can walk on both water and land. For instance Water Elementals. Add WATERCREATURE to creatures.dfn, and they'll be able to swim, but not walk on land. Remove it, and they can walk on land but not on water.
Personally, I believe many things all ready suffer from tag explosion.
How about a tag that is like:
MOVEMENT LAND
or
MOVEMENT WATER
or
MOVEMENT BOTH ?
It seems to be easer it "key" , and if MOVEMENT is not there, defaults to land?
How about a tag that is like:
MOVEMENT LAND
or
MOVEMENT WATER
or
MOVEMENT BOTH ?
It seems to be easer it "key" , and if MOVEMENT is not there, defaults to land?
-
giwo
- Developer
- Posts: 1780
- Joined: Fri Jun 18, 2004 4:17 pm
- Location: California
- Has thanked: 0
- Been thanked: 0
I'm with punt on this one.
Reading in DFN's like creatures become more and more complex with added tags. Obviously there is a need to flag which creatures can move where, but rather than adding a different tag for each one (which people coming along later will have to interpret) one tag with different settings seems the wiser way to go.
Reading in DFN's like creatures become more and more complex with added tags. Obviously there is a need to flag which creatures can move where, but rather than adding a different tag for each one (which people coming along later will have to interpret) one tag with different settings seems the wiser way to go.
Scott
-
giwo
- Developer
- Posts: 1780
- Joined: Fri Jun 18, 2004 4:17 pm
- Location: California
- Has thanked: 0
- Been thanked: 0
Xu: That's based off of cMapStuff::CanMonsterMoveHere()
The spawnregion will simply try to place the NPC randomly in the region, if it finds it as a valid tile where the monster can walk, then it will place it, be it on water, land, a hillside, etc.
So basically, it depends on if DoesStaticBlock() allows for "water" creatures.
The spawnregion will simply try to place the NPC randomly in the region, if it finds it as a valid tile where the monster can walk, then it will place it, be it on water, land, a hillside, etc.
So basically, it depends on if DoesStaticBlock() allows for "water" creatures.
Scott
-
Grimson
- Developer
- Posts: 802
- Joined: Sat Jun 04, 2005 1:52 am
- Location: Germany
- Has thanked: 0
- Been thanked: 0
I changed the CanFishWalk function this way:
If I understood the code right this should give me the statics on the x,y coordinate in the world, and check wheter there is a non wet static and then return false, but it doesn't work
. Well that part of the code is quite complicated and barely commented...
Code: Select all
bool cMovement::CanFishWalk( CTileUni *tb, SI16 x, SI16 y, UI08 worldNumber )
{
// UI16 blockid = tb->ID();
CTile tile;
MapStaticIterator msi( x, y, worldNumber );
staticrecord *stat = NULL;
if( tb->LiquidWet() )
{
while ( stat = msi.Next() )
{
Map->SeekTile(stat->itemid, &tile);
if( !tile.LiquidWet() )
return false;
}
return true;
}
return false;
}
Are you only checking the Art tile? What about the terrain tiles? I could build a pier (with Art a.k.a static tiles) but have the base ocean be made with terrain tiles.
Not saying that is your problem here, dind't look into the code, but from just the name of your snippet, it seemd you have to check both terrain and art tiles.
Not saying that is your problem here, dind't look into the code, but from just the name of your snippet, it seemd you have to check both terrain and art tiles.
-
Grimson
- Developer
- Posts: 802
- Joined: Sat Jun 04, 2005 1:52 am
- Location: Germany
- Has thanked: 0
- Been thanked: 0
If I got it right the tile "tb" is the terrain tile, but I'm not sure.punt wrote:Are you only checking the Art tile? What about the terrain tiles? I could build a pier (with Art a.k.a static tiles) but have the base ocean be made with terrain tiles.
Ok, but let me ask the opposite.
If the terrain isn't wet (like shallow water), it will never check the statics.
I realize you are looking for another issue, but still trying to understand the basic setup.
If the terrain isn't wet (like shallow water), it will never check the statics.
I realize you are looking for another issue, but still trying to understand the basic setup.
-
Grimson
- Developer
- Posts: 802
- Joined: Sat Jun 04, 2005 1:52 am
- Location: Germany
- Has thanked: 0
- Been thanked: 0
Yes, that's how it is supposed to work. Or are there any statics in which they should be able to swim?punt wrote:Ok, but let me ask the opposite.
If the terrain isn't wet (like shallow water), it will never check the statics.