Форум администраторов игровых серверов

Форум администраторов игровых серверов (https://forum.zone-game.info/TT.php)
-   Lineage II (https://forum.zone-game.info/forumdisplay.php?f=34)
-   -   Работа над Goddess of Destruction (part 5) (https://forum.zone-game.info/showthread.php?t=17274)

Erlandas 23.02.2012 20:39

Re: Работа над Goddess of Destruction (part 5)
 
Thank you! But then, what have been changed in new system? :O

Darvin 29.02.2012 14:49

Re: Работа над Goddess of Destruction (part 5)
 
Коллеги поделитесь пожвлуйста пакетом для квестов, а то чёт не могу понять ничё с ним.

ANZO 29.02.2012 15:42

Re: Работа над Goddess of Destruction (part 5)
 
Jq:
Свернуть ↑Развернуть ↓

iquelite 02.03.2012 22:48

Re: Работа над Goddess of Destruction (part 5)
 
http://rghost.net/36815680/image.png
anyone help me fix this
i changed VP system ,i can see 140000 points in db ,but do not show the VP while login
ExLoginVitalityEffectInfo
Код:

package l2p.gameserver.serverpackets;

import l2p.gameserver.Config;
import l2p.gameserver.model.CharSelectionInfo;

public class ExLoginVitalityEffectInfo extends L2GameServerPacket {

    private CharSelectionInfo charInfo;

    public ExLoginVitalityEffectInfo(CharSelectionInfo charInfo) {
        this.charInfo = charInfo;
    }

    @Override
    protected void writeImpl() {
        writeEx(0x11E);

        writeD(charInfo.getVitalityPoints() == 0 ? 0 : (int) (Config.ALT_VITALITY_RATE * 100)); // Exp bonus
        writeD(5); // TODO
    }
}


shocked 03.03.2012 00:12

Re: Работа над Goddess of Destruction (part 5)
 
in login screen vitality point sends in CharacterSelectionInfo packet (opcode 0x09)

iquelite 03.03.2012 09:07

Re: Работа над Goddess of Destruction (part 5)
 
Цитата:

Сообщение от JackTheRipp (Сообщение 168449)
in login screen vitality point sends in CharacterSelectionInfo packet (opcode 0x09)

CharacterSelectionInfo already done, but still not work
Код:

            writeD(charSelectionInfo.getVitalityPoints());
            writeD(charInfoPackage.getAccessLevel() > -100 ? 0x01 : 0x00);// Активен ли.

            i++;
        }
    }

    public static CharSelectionInfo loadCharacterSelectInfo(String loginName) {
        CharSelectionInfo charSelectionInfo = new CharSelectionInfo();

        Connection con = null;
        PreparedStatement statement = null;
        ResultSet rset = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.prepareStatement("SELECT * FROM characters AS c LEFT JOIN character_subclasses AS cs ON (c.obj_Id=cs.char_obj_id AND cs.active=1) WHERE account_name=? LIMIT 7");
            statement.setString(1, loginName);
            rset = statement.executeQuery();
            while (rset.next()) { // fills the package
                charSelectionInfo.addSelectionInfo(restoreChar(rset));
            }
            DbUtils.closeQuietly(statement, rset);
            statement = con.prepareStatement("SELECT `points` FROM `vitality_points` WHERE `account_name`=?");
            statement.setString(1, loginName);
            rset = statement.executeQuery();
            if (rset.next()) {
                int points = rset.getInt(1);
                charSelectionInfo.setVitalityPoints(points);
            }
        } catch (Exception e) {
            _log.error("could not restore charinfo:", e);
        } finally {
            DbUtils.closeQuietly(con, statement, rset);
        }

        return charSelectionInfo;
    }


ANZO 03.03.2012 10:49

Re: Работа над Goddess of Destruction (part 5)
 
Send it in LoginServerThread, buddy.

shocked 03.03.2012 11:24

Re: Работа над Goddess of Destruction (part 5)
 
http://rghost.ru/36820325/image.png

PHP код:

...
writeC(Math.min(charInfoPackage.getPaperdollEnchantEffect(Inventory.PAPERDOLL_RHAND), 127));
            
writeD(charInfoPackage.getPaperdollAugmentationId(Inventory.PAPERDOLL_RHAND));
            
int weaponId charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_RHAND);
            if(
weaponId == 8190)
                
writeD(301);
            else if(
weaponId == 8689)
                
writeD(302);
            else
                
writeD(0x00);

            
writeD(0x00);
            
writeD(0x00);
            
writeD(0x00);
            
writeD(0x00);
            
writeF(0x00);
            
writeF(0x00);
            
writeD(140000); //FXIME VitalityPoints
                        
writeD(0x1); // unk GOD 


ALF. 03.03.2012 12:36

Re: Работа над Goddess of Destruction (part 5)
 
Ребят, подскажите пожалуйста в пет инфо в ГоДе последние ddd это что?
Нуу точнее
PHP код:

        writeD(0x00); <--- ЧТО ЭТО?)
        
writeD(sumPoint);
        
writeD(maxSumPoint); 

ии
Исправьте пожалуйста мой
PHP код:

package l2p.gameserver.network.serverpackets;

import l2p.gameserver.model.Summon;

public class 
PetStatusShow extends L2GameServerPacket
{
    private 
int _summonType;
    private 
int _summonId;

    public 
PetStatusShow(Summon summon)
    {
        
_summonType summon.getSummonType();
        
_summonId summon.getObjectId();
    }

    @
Override
    
protected final void writeImpl()
    {
        
writeC(0xb1);
        
writeD(_summonType);
        
writeD(_summonId);//L2WT GOD
    
}



darkevil 03.03.2012 14:50

Re: Работа над Goddess of Destruction (part 5)
 
Цитата:

Сообщение от ALFOS (Сообщение 168493)
Ребят, подскажите пожалуйста в пет инфо в ГоДе последние ddd это что?
Нуу точнее
PHP код:

        writeD(0x00); <--- ЧТО ЭТО?)
        
writeD(sumPoint);
        
writeD(maxSumPoint); 

ии
Исправьте пожалуйста мой
PHP код:

package l2p.gameserver.network.serverpackets;

import l2p.gameserver.model.Summon;

public class 
PetStatusShow extends L2GameServerPacket
{
    private 
int _summonType;
    private 
int _summonId;

    public 
PetStatusShow(Summon summon)
    {
        
_summonType summon.getSummonType();
        
_summonId summon.getObjectId();
    }

    @
Override
    
protected final void writeImpl()
    {
        
writeC(0xb1);
        
writeD(_summonType);
        
writeD(_summonId);//L2WT GOD
    
}



В первом случае это что-то вроде трансформ, под ПКХ ид подставляй туда, петы будут трансформироваться. PetStatusShow у тебя верный.

iquelite 03.03.2012 19:56

Re: Работа над Goddess of Destruction (part 5)
 
sorry for Made a mistake in CharSelectionInfo
setVitalityPoints(int points)
the Variable points are different named in CharacterSelectionInfo OMG...;)

jalemao 04.03.2012 08:06

Re: Работа над Goddess of Destruction (part 5)
 
Hi Guys,

An little question...

How did u defined the Races Stats for every Awk Class?

Example :
Yul Archer - Kamael Race ( Kamael Fighter Stats (STR/DEX/CON/ETC...))
Yul Archer - Human Race ( Human Fighter Stats (STR/DEX/CON/ETC...))

Thanks in advance!

iquelite 04.03.2012 08:57

Re: Работа над Goddess of Destruction (part 5)
 
Цитата:

Сообщение от jalemao (Сообщение 168647)
Hi Guys,

An little question...

How did u defined the Races Stats for every Awk Class?

Example :
Yul Archer - Kamael Race ( Kamael Fighter Stats (STR/DEX/CON/ETC...))
Yul Archer - Human Race ( Human Fighter Stats (STR/DEX/CON/ETC...))

Thanks in advance!

char_templates.sql i think

jalemao 04.03.2012 09:03

Re: Работа над Goddess of Destruction (part 5)
 
Yes u can put base stats there but not the Races Stats of each Class... ^^

iquelite 04.03.2012 11:21

Re: Работа над Goddess of Destruction (part 5)
 
Цитата:

Сообщение от jalemao (Сообщение 168649)
Yes u can put base stats there but not the Races Stats of each Class... ^^

i tested ,it work ,you should change whatever you want to change.
by the way according to CT3, the same base stats , only the satas info*2 , and Fewer changes in hennas .

jalemao 05.03.2012 01:39

Re: Работа над Goddess of Destruction (part 5)
 
Yes it works... but if u put 139 ( Sigel Knight ) u will need to put stats there, and u can put only of one race... Like Human, haves 88 STR... but u cant put Sigel Knight for all Races there...

Yorie 05.03.2012 08:30

Re: Работа над Goddess of Destruction (part 5)
 
Цитата:

Hi Guys,

An little question...

How did u defined the Races Stats for every Awk Class?

Example :
Yul Archer - Kamael Race ( Kamael Fighter Stats (STR/DEX/CON/ETC...))
Yul Archer - Human Race ( Human Fighter Stats (STR/DEX/CON/ETC...))

Thanks in advance!
One method is define all variants of new classes in char_templates (there're about 36 new classes). Another opportunity is linking parent class ID after character awakening (for example, store this value in character table). In that case you'll need to change some SQL queries to let it work.

jalemao 05.03.2012 11:07

Re: Работа над Goddess of Destruction (part 5)
 
Hum, lets do it ^^

Thanks!


Other question... does some packet changed to the HTML? i have some HTML thats dont work in Goddess...

Yorie 05.03.2012 11:19

Re: Работа над Goddess of Destruction (part 5)
 
Цитата:

Other question... does some packet changed to the HTML? i have some HTML thats dont work in Goddess...
Don't sure, what's going on with HTML interpretation on client side, but I guess that GoD client becomes with more strict HTML validation. I had problems with several old HTML files too. Just check out where's error by cutting out parts of HTML file until it will shown normally. Then you'll get the problem place and this, in most cases, will be the same problem in all other files that are not parsed by client.

jalemao 05.03.2012 11:24

Re: Работа над Goddess of Destruction (part 5)
 
Well at all its strange :s

About the Classes... im not the skill'd with Core,SQL, more with DP... do u have any example for me? :)

ANZO 05.03.2012 12:27

Re: Работа над Goddess of Destruction (part 5)
 
Without core's dev skills you will have some problems. You need to create at least 36 different ID for new professions (server side ID's) and the method is interpreted them to the client (139-146).

jalemao 05.03.2012 12:31

Re: Работа над Goddess of Destruction (part 5)
 
That why i makd the question, coz what i know its that the Client part dont have more Dummys to make it sooo easy... Well at all... My Hobby continues :D Resolve Questions/problems on L2p/j/dc :D

PS: i know about core/java and all the other things... but my GOLD part is DP!

ALF. 06.03.2012 01:14

Re: Работа над Goddess of Destruction (part 5)
 
PHP код:

        writeC(0xb1);
        
writeD(_summonType);
        
writeD(_summonId);//L2WT GOD 

А не на оборот?...
А то у меня когда вызвано много петов - то у всех показывает одинаковые скилы - действия....
Или это уже в другом пакете? оО

Erlandas 06.03.2012 08:14

Re: Работа над Goddess of Destruction (part 5)
 
Hello, again I have question.. :)
Maybe someone have MultisellList packet structure? For Dyes Manager? I can't find out what structure it is... :/

ALF. 06.03.2012 08:56

Re: Работа над Goddess of Destruction (part 5)
 
Цитата:

Сообщение от Erlandas (Сообщение 168948)
Hello, again I have question.. :)
Maybe someone have MultisellList packet structure? For Dyes Manager? I can't find out what structure it is... :/

PHP код:

        writeD(_listId); // list id
        
writeD(_page); // page
        
writeD(_finished); // finished
        
writeD(Config.MULTISELL_SIZE); // size of pages
        
writeD_list.size()); //list length
        
writeC(_isnew 0x01 0x00); 

IF (_isnew == 1) { we have a new multisell.}

Erlandas 06.03.2012 15:43

Re: Работа над Goddess of Destruction (part 5)
 
You see I need only this part, I know everything else:
http://img256.imageshack.us/img256/9747/shot00021tk.jpg

PSIFAK 06.03.2012 19:42

Re: Работа над Goddess of Destruction (part 5)
 
Подскажите что это за пакет чет намудрил сейчас критует клиент постоянно если нового перса создать всё норм но как несколько лвл апну опять такая шняга

[STIGMATED] 06.03.2012 19:46

Re: Работа над Goddess of Destruction (part 5)
 
Вырезайте невита.

Gaikotsu 07.03.2012 00:15

Re: Работа над Goddess of Destruction (part 5)
 
поделитесь кто нибудь нормальными ddf-файлами для l2asm/disasm, которыми можно раскодить dat-файлы евроклиента GoD'а.
а то блин сколько не искал в инете - одно нерабочее г... попадается...

можно даже не все - мне нужны только 2 больше всего - для itemname-e и weapongrp

З.Ы. ну или можно просто уже раскоденные в текстовый вид эти dat-файлы.

[STIGMATED] 08.03.2012 21:50

Re: Работа над Goddess of Destruction (part 5)
 
Gaikotsu, http://rghost.ru/36917599 , fileEdit, которым я пользуюсь.

Может кто скинет дамп 415 или 410(Harmony Euro) протокола, а-то не найду.

PROGRAMMATOR 08.03.2012 23:45

Re: Работа над Goddess of Destruction (part 5)
 
Было же у меня в подписи. (структуры сверять в ручную, ибо они попутаны)
415_Client_Packets_Struct_Opcode.7z
415_Server_Packets_Stuct_Opcode.7z
415_Core_With_GameGuard.7z
415_DisAsm_Packets_Engine.7z
415_Text_Strings_Referenced_In_Engine.7z

Darvin 10.03.2012 03:08

Re: Работа над Goddess of Destruction (part 5)
 
может я нуб и тд, но всёже вот гео енжин для оверов под гео года
PHP код:

package l2p.gameserver.geodata;

import l2p.commons.geometry.Shape;
import l2p.gameserver.Config;
import l2p.gameserver.geodata.GeoOptimizer.BlockLink;
import l2p.gameserver.model.GameObject;
import l2p.gameserver.model.World;
import l2p.gameserver.utils.Location;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: Diamond
 * @CoAuthor: DRiN
 * @Date: 01/03/2009
 */
public class GeoEngine {
    private static final 
Logger _log LoggerFactory.getLogger(GeoEngine.class);

    public static final 
byte EAST 1WEST 2SOUTH 4NORTH 8NSWE_ALL 15NSWE_NONE 0;

    public static final 
byte BLOCKTYPE_FLAT 0;
    public static final 
byte BLOCKTYPE_COMPLEX 1;
    public static final 
byte BLOCKTYPE_MULTILEVEL 2;

    public static final 
int BLOCKS_IN_MAP 256 256;

    public static 
int MAX_LAYERS 1// меньше 1 быть не должно, что бы создавались временные массивы как минимум short[2]

    /**
     * Даный массив содержит эталонную геодату. <BR>
     * Первые 2 [][] (byte[*][*][][]) являются x и y региона.<BR>
     */
    
private static final MappedByteBuffer[][] rawgeo = new MappedByteBuffer[World.WORLD_SIZE_X][World.WORLD_SIZE_Y];

    
/**
     * Даный массив содержит всю геодату на сервере. <BR>
     * Первые 2 [][] (byte[*][*][][]) являются x и y региона.<BR>
     * Третий [] (byte[][][*][]) является блоком геодаты.<BR>
     * Четвертый [] (byte[][][][*]) является контейнером для всех блоков в регионе.<BR>
     */
    
private static final byte[][][][][] geodata = new byte[World.WORLD_SIZE_X][World.WORLD_SIZE_Y][][][];

    public static 
short getType(int xint yint geoIndex) {
        return 
NgetType(World.MAP_MIN_X >> 4World.MAP_MIN_Y >> 4geoIndex);
    }

    public static 
int getHeight(Location locint geoIndex) {
        return 
getHeight(loc.xloc.yloc.zgeoIndex);
    }

    public static 
int getHeight(int xint yint zint geoIndex) {
        return 
NgetHeight(World.MAP_MIN_X >> 4World.MAP_MIN_Y >> 4zgeoIndex);
    }

    public static 
boolean canMoveToCoord(int xint yint zint txint tyint tzint geoIndex) {
        return 
canMove(xyztxtytzfalsegeoIndex) == 0;
    }

    public static 
byte getNSWE(int xint yint zint geoIndex) {
        return 
NgetNSWE(World.MAP_MIN_X >> 4World.MAP_MIN_Y >> 4zgeoIndex);
    }

    public static 
Location moveCheck(int xint yint zint txint tyint geoIndex) {
        return 
MoveCheck(xyztxtyfalsefalsefalsegeoIndex);
    }

    public static 
Location moveCheck(int xint yint zint txint tyboolean returnPrevint geoIndex) {
        return 
MoveCheck(xyztxtyfalsefalsereturnPrevgeoIndex);
    }

    public static 
Location moveCheckWithCollision(int xint yint zint txint tyint geoIndex) {
        return 
MoveCheck(xyztxtytruefalsefalsegeoIndex);
    }

    public static 
Location moveCheckWithCollision(int xint yint zint txint tyboolean returnPrevint geoIndex) {
        return 
MoveCheck(xyztxtytruefalsereturnPrevgeoIndex);
    }

    public static 
Location moveCheckBackward(int xint yint zint txint tyint geoIndex) {
        return 
MoveCheck(xyztxtyfalsetruefalsegeoIndex);
    }

    public static 
Location moveCheckBackward(int xint yint zint txint tyboolean returnPrevint geoIndex) {
        return 
MoveCheck(xyztxtyfalsetruereturnPrevgeoIndex);
    }

    public static 
Location moveCheckBackwardWithCollision(int xint yint zint txint tyint geoIndex) {
        return 
MoveCheck(xyztxtytruetruefalsegeoIndex);
    }

    public static 
Location moveCheckBackwardWithCollision(int xint yint zint txint tyboolean returnPrevint geoIndex) {
        return 
MoveCheck(xyztxtytruetruereturnPrevgeoIndex);
    }

    public static 
Location moveInWaterCheck(int xint yint zint txint tyint tzint waterZint geoIndex) {
        return 
MoveInWaterCheck(World.MAP_MIN_X >> 4World.MAP_MIN_Y >> 4ztx World.MAP_MIN_X >> 4ty World.MAP_MIN_Y >> 4tzwaterZgeoIndex);
    }

    public static 
Location moveCheckForAI(Location loc1Location loc2int geoIndex) {
        return 
MoveCheckForAI(loc1.World.MAP_MIN_X >> 4loc1.World.MAP_MIN_Y >> 4loc1.zloc2.World.MAP_MIN_X >> 4loc2.World.MAP_MIN_Y >> 4geoIndex);
    }

    public static 
Location moveCheckInAir(int xint yint zint txint tyint tzdouble collisionint geoIndex) {
        
int gx World.MAP_MIN_X >> 4;
        
int gy World.MAP_MIN_Y >> 4;
        
int tgx tx World.MAP_MIN_X >> 4;
        
int tgy ty World.MAP_MIN_Y >> 4;

        
int nz NgetHeight(tgxtgytzgeoIndex);

        
// Не даем опуститься ниже, чем пол + 32
        
if (tz <= nz 32)
            
tz nz 32;

        
Location result canSee(gxgyztgxtgytztruegeoIndex);
        if (
result.equals(gxgyz))
            return 
null;

        return 
result.geo2world();
    }

    public static 
boolean canSeeTarget(GameObject actorGameObject targetboolean air) {
        if (
target == null)
            return 
false;
        
// Костыль конечно, но решает кучу проблем с дверьми
        
if (target instanceof GeoCollision || actor.equals(target))
            return 
true;
        return 
canSeeCoord(actortarget.getX(), target.getY(), target.getZ() + (int) target.getColHeight() + 32air);
    }

    public static 
boolean canSeeCoord(GameObject actorint txint tyint tzboolean air) {
        return 
actor != null && canSeeCoord(actor.getX(), actor.getY(), actor.getZ() + (int) actor.getColHeight() + 32txtytzairactor.getGeoIndex());
    }

    public static 
boolean canSeeCoord(int xint yint zint txint tyint tzboolean airint geoIndex) {
        
int mx World.MAP_MIN_X >> 4;
        
int my World.MAP_MIN_Y >> 4;
        
int tmx tx World.MAP_MIN_X >> 4;
        
int tmy ty World.MAP_MIN_Y >> 4;
        return 
canSee(mxmyztmxtmytzairgeoIndex).equals(tmxtmytz) && canSee(tmxtmytzmxmyzairgeoIndex).equals(mxmyz);
    }

    public static 
boolean canMoveWithCollision(int xint yint zint txint tyint tzint geoIndex) {
        return 
canMove(xyztxtytztruegeoIndex) == 0;
    }

    
/**
     * @param NSWE
     * @param x
     * @param y
     * @param tx
     * @param ty
     * @return True if NSWE dont block given direction
     */
    
public static boolean checkNSWE(byte NSWEint xint yint txint ty) {
        if (
NSWE == NSWE_ALL)
            return 
true;
        if (
NSWE == NSWE_NONE)
            return 
false;
        if (
tx x) {
            if ((
NSWE EAST) == 0)
                return 
false;
        } else if (
tx x)
            if ((
NSWE WEST) == 0)
                return 
false;
        if (
ty y) {
            if ((
NSWE SOUTH) == 0)
                return 
false;
        } else if (
ty y)
            if ((
NSWE NORTH) == 0)
                return 
false;
        return 
true;
    }

    public static 
String geoXYZ2Str(int _xint _yint _z) {
        return 
"(" String.valueOf((_x << 4) + World.MAP_MIN_X 8) + " " String.valueOf((_y << 4) + World.MAP_MIN_Y 8) + " " _z ")";
    }

    public static 
String NSWE2Str(byte nswe) {
        
String result "";
        if ((
nswe NORTH) == NORTH)
            
result += "N";
        if ((
nswe SOUTH) == SOUTH)
            
result += "S";
        if ((
nswe WEST) == WEST)
            
result += "W";
        if ((
nswe EAST) == EAST)
            
result += "E";
        return 
result.isEmpty() ? "X" result;
    }

    private static 
boolean NLOS_WATER(int xint yint zint next_xint next_yint next_zint geoIndex) {
        
short[] layers1 = new short[MAX_LAYERS 1];
        
short[] layers2 = new short[MAX_LAYERS 1];
        
NGetLayers(xylayers1geoIndex);
        
NGetLayers(next_xnext_ylayers2geoIndex);

        if (
layers1[0] == || layers2[0] == 0)
            return 
true;

        
short h;

        
// Находим ближайший к целевой клетке слой
        
short z2 Short.MIN_VALUE;
        for (
int i 1<= layers2[0]; i++) {
            
= (short) ((short) (layers2[i] & 0x0fff0) >> 1);
            if (
Math.abs(next_z z2) > Math.abs(next_z h))
                
z2 h;
        }

        
// Луч проходит над преградой
        
if (next_z 32 >= z2)
            return 
true;

        
// Либо перед нами стена, либо над нами потолок. Ищем слой пониже, для уточнения
        
short z3 Short.MIN_VALUE;
        for (
int i 1<= layers2[0]; i++) {
            
= (short) ((short) (layers2[i] & 0x0fff0) >> 1);
            if (
z2 Config.MIN_LAYER_HEIGHT && Math.abs(next_z z3) > Math.abs(next_z h))
                
z3 h;
        }

        
// Ниже нет слоев, значит это стена
        
if (z3 == Short.MIN_VALUE)
            return 
false;

        
// Собираем данные о предыдущей клетке, игнорируя верхние слои
        
short z1 Short.MIN_VALUE;
        
byte NSWE1 NSWE_ALL;
        for (
int i 1<= layers1[0]; i++) {
            
= (short) ((short) (layers1[i] & 0x0fff0) >> 1);
            if (
Config.MIN_LAYER_HEIGHT && Math.abs(z1) > Math.abs(h)) {
                
z1 h;
                
NSWE1 = (byte) (layers1[i] & 0x0F);
            }
        }

        
// Если есть NSWE, то считаем за стену
        
return checkNSWE(NSWE1xynext_xnext_y);
    }

    private static 
int FindNearestLowerLayer(short[] layersint z) {
        
short hnearest_layer_h Short.MIN_VALUE;
        
int nearest_layer Integer.MIN_VALUE;
        for (
int i 1<= layers[0]; i++) {
            
= (short) ((short) (layers[i] & 0x0fff0) >> 1);
            if (
&& nearest_layer_h h) {
                
nearest_layer_h h;
                
nearest_layer layers[i];
            }
        }
        return 
nearest_layer;
    }

    private static 
short CheckNoOneLayerInRangeAndFindNearestLowerLayer(short[] layersint z0int z1) {
        
int z_minz_max;
        if (
z0 z1) {
            
z_min z1;
            
z_max z0;
        } else {
            
z_min z0;
            
z_max z1;
        }
        
short hnearest_layer Short.MIN_VALUEnearest_layer_h Short.MIN_VALUE;
        for (
int i 1<= layers[0]; i++) {
            
= (short) ((short) (layers[i] & 0x0fff0) >> 1);
            if (
z_min <= && <= z_max)
                return 
Short.MIN_VALUE;
            if (
z0 && nearest_layer_h h) {
                
nearest_layer_h h;
                
nearest_layer layers[i];
            }
        }
        return 
nearest_layer;
    }

    public static 
boolean canSeeWallCheck(short layershort nearest_lower_neighborbyte directionNSWEint curr_zboolean air) {
        
short nearest_lower_neighborh = (short) ((short) (nearest_lower_neighbor 0x0fff0) >> 1);
        if (
air)
            return 
nearest_lower_neighborh curr_z;
        
short layerh = (short) ((short) (layer 0x0fff0) >> 1);
        
int zdiff nearest_lower_neighborh layerh;
        return (
layer 0x0F directionNSWE) != || zdiff > -Config.MAX_Z_DIFF && zdiff != 0;
    }

    
/**
     * проверка видимости
     *
     * @return возвращает последнюю точку которую видно (в формате геокоординат)
     *         в результате (Location) h является кодом, если >= 0 то успешно достигли последней точки, если меньше то не последней
     */
    
public static Location canSee(int _xint _yint _zint _txint _tyint _tzboolean airint geoIndex) {
        
int diff_x _tx _xdiff_y _ty _ydiff_z _tz _z;
        
int dx Math.abs(diff_x), dy Math.abs(diff_y);

        
float steps Math.max(dxdy);
        
int curr_x _xcurr_y _ycurr_z _z;
        
short[] curr_layers = new short[MAX_LAYERS 1];
        
NGetLayers(curr_xcurr_ycurr_layersgeoIndex);

        
Location result = new Location(_x_y_z, -1);

        if (
steps == 0) {
            if (
CheckNoOneLayerInRangeAndFindNearestLowerLayer(curr_layerscurr_zcurr_z diff_z) != Short.MIN_VALUE)
                
result.set(_tx_ty_tz1);
            return 
result;
        }

        
float step_x diff_x stepsstep_y diff_y stepsstep_z diff_z steps;
        
float half_step_z step_z 2.0f;
        
float next_x curr_xnext_y curr_ynext_z curr_z;
        
int i_next_xi_next_yi_next_zmiddle_z;
        
short[] tmp_layers = new short[MAX_LAYERS 1];
        
short src_nearest_lower_layerdst_nearest_lower_layertmp_nearest_lower_layer;

        for (
int i 0stepsi++) {
            if (
curr_layers[0] == 0) {
                
result.set(_tx_ty_tz0);
                return 
result// Здесь нет геодаты, разрешаем
            
}

            
next_x += step_x;
            
next_y += step_y;
            
next_z += step_z;
            
i_next_x = (int) (next_x 0.5f);
            
i_next_y = (int) (next_y 0.5f);
            
i_next_z = (int) (next_z 0.5f);
            
middle_z = (int) (curr_z half_step_z);

            if ((
src_nearest_lower_layer CheckNoOneLayerInRangeAndFindNearestLowerLayer(curr_layerscurr_zmiddle_z)) == Short.MIN_VALUE)
                return 
result.setH(-10); // либо есть преграждающая поверхность, либо нет снизу слоя и значит это "пустота", то что за стеной или за колоной

            
NGetLayers(curr_xcurr_ycurr_layersgeoIndex);
            if (
curr_layers[0] == 0) {
                
result.set(_tx_ty_tz0);
                return 
result// Здесь нет геодаты, разрешаем
            
}

            if ((
dst_nearest_lower_layer CheckNoOneLayerInRangeAndFindNearestLowerLayer(curr_layersi_next_zmiddle_z)) == Short.MIN_VALUE)
                return 
result.setH(-11); // либо есть преграда, либо нет снизу слоя и значит это "пустота", то что за стеной или за колоной

            
if (curr_x == i_next_x) {
                
//движемся по вертикали
                
if (!canSeeWallCheck(src_nearest_lower_layerdst_nearest_lower_layeri_next_y curr_y SOUTH NORTHcurr_zair))
                    return 
result.setH(-20);
            } else if (
curr_y == i_next_y) {
                
//движемся по горизонтали
                
if (!canSeeWallCheck(src_nearest_lower_layerdst_nearest_lower_layeri_next_x curr_x EAST WESTcurr_zair))
                    return 
result.setH(-21);
            } else {
                
//движемся по диагонали
                
NGetLayers(curr_xi_next_ytmp_layersgeoIndex);
                if (
tmp_layers[0] == 0) {
                    
result.set(_tx_ty_tz0);
                    return 
result// Здесь нет геодаты, разрешаем
                
}
                if ((
tmp_nearest_lower_layer CheckNoOneLayerInRangeAndFindNearestLowerLayer(tmp_layersi_next_zmiddle_z)) == Short.MIN_VALUE)
                    return 
result.setH(-30); // либо есть преграда, либо нет снизу слоя и значит это "пустота", то что за стеной или за колоной

                
if (!(canSeeWallCheck(src_nearest_lower_layertmp_nearest_lower_layeri_next_y curr_y SOUTH NORTHcurr_zair) && canSeeWallCheck(tmp_nearest_lower_layerdst_nearest_lower_layeri_next_x curr_x EAST WESTcurr_zair))) {
                    
NGetLayers(i_next_xcurr_ytmp_layersgeoIndex);
                    if (
tmp_layers[0] == 0) {
                        
result.set(_tx_ty_tz0);
                        return 
result// Здесь нет геодаты, разрешаем
                    
}
                    if ((
tmp_nearest_lower_layer CheckNoOneLayerInRangeAndFindNearestLowerLayer(tmp_layersi_next_zmiddle_z)) == Short.MIN_VALUE)
                        return 
result.setH(-31); // либо есть преграда, либо нет снизу слоя и значит это "пустота", то что за стеной или за колоной
                    
if (!canSeeWallCheck(src_nearest_lower_layertmp_nearest_lower_layeri_next_x curr_x EAST WESTcurr_zair))
                        return 
result.setH(-32);
                    if (!
canSeeWallCheck(tmp_nearest_lower_layerdst_nearest_lower_layeri_next_x curr_x EAST WESTcurr_zair))
                        return 
result.setH(-33);
                }
            }

            
result.set(curr_xcurr_ycurr_z);
            
curr_x i_next_x;
            
curr_y i_next_y;
            
curr_z i_next_z;
        }

        
result.set(_tx_ty_tz0xFF);
        return 
result;
    }

    private static 
Location MoveInWaterCheck(int xint yint zint txint tyint tzint waterZint geoIndex) {
        
int dx tx x;
        
int dy ty y;
        
int dz tz z;
        
int inc_x sign(dx);
        
int inc_y sign(dy);
        
dx Math.abs(dx);
        
dy Math.abs(dy);
        if (
dx dy == 0)
            return new 
Location(xyz).geo2world();
        
float inc_z_for_x dx == dz dx;
        
float inc_z_for_y dy == dz dy;
        
int prev_x;
        
int prev_y;
        
int prev_z;
        
float next_x x;
        
float next_y y;
        
float next_z z;
        if (
dx >= dy// dy/dx <= 1
        
{
            
int delta_A dy;
            
int d delta_A dx;
            
int delta_B delta_A dx;
            for (
int i 0dxi++) {
                
prev_x x;
                
prev_y y;
                
prev_z z;
                
= (int) next_x;
                
= (int) next_y;
                
= (int) next_z;
                if (
0) {
                    
+= delta_B;
                    
next_x += inc_x;
                    
next_z += inc_z_for_x;
                    
next_y += inc_y;
                    
next_z += inc_z_for_y;
                } else {
                    
+= delta_A;
                    
next_x += inc_x;
                    
next_z += inc_z_for_x;
                }

                if (
next_z >= waterZ || !NLOS_WATER(xyz, (int) next_x, (int) next_y, (int) next_zgeoIndex))
                    return new 
Location(prev_xprev_yprev_z).geo2world();
            }
        } else {
            
int delta_A dx;
            
int d delta_A dy;
            
int delta_B delta_A dy;
            for (
int i 0dyi++) {
                
prev_x x;
                
prev_y y;
                
prev_z z;
                
= (int) next_x;
                
= (int) next_y;
                
= (int) next_z;
                if (
0) {
                    
+= delta_B;
                    
next_x += inc_x;
                    
next_z += inc_z_for_x;
                    
next_y += inc_y;
                    
next_z += inc_z_for_y;
                } else {
                    
+= delta_A;
                    
next_y += inc_y;
                    
next_z += inc_z_for_y;
                }

                if (
next_z >= waterZ || !NLOS_WATER(xyz, (int) next_x, (int) next_y, (int) next_zgeoIndex))
                    return new 
Location(prev_xprev_yprev_z).geo2world();
            }
        }
        return new 
Location((int) next_x, (int) next_y, (int) next_z).geo2world();
    }

    
/**
     * проверка проходимости по прямой
     *
     * @return 0 - проходимо, в ином случае код причины непроходимости (используется при отладке)
     */
    
private static int canMove(int __xint __yint _zint __txint __tyint _tzboolean withCollisionint geoIndex) {
        
int _x __x World.MAP_MIN_X >> 4;
        
int _y __y World.MAP_MIN_Y >> 4;
        
int _tx __tx World.MAP_MIN_X >> 4;
        
int _ty __ty World.MAP_MIN_Y >> 4;
        
int diff_x _tx _xdiff_y _ty _ydiff_z _tz _z;
        
int dx Math.abs(diff_x), dy Math.abs(diff_y), dz Math.abs(diff_z);
        
float steps Math.max(dxdy);
        if (
steps == 0)
            return -
5;

        
int curr_x _xcurr_y _ycurr_z _z;
        
short[] curr_layers = new short[MAX_LAYERS 1];
        
NGetLayers(curr_xcurr_ycurr_layersgeoIndex);
        if (
curr_layers[0] == 0)
            return 
0;

        
float step_x diff_x stepsstep_y diff_y steps;
        
float next_x curr_xnext_y curr_y;
        
int i_next_xi_next_y;

        
short[] next_layers = new short[MAX_LAYERS 1];
        
short[] temp_layers = new short[MAX_LAYERS 1];
        
short[] curr_next_switcher;

        for (
int i 0stepsi++) {
            
next_x += step_x;
            
next_y += step_y;
            
i_next_x = (int) (next_x 0.5f);
            
i_next_y = (int) (next_y 0.5f);
            
NGetLayers(i_next_xi_next_ynext_layersgeoIndex);
            if ((
curr_z NcanMoveNext(curr_xcurr_ycurr_zcurr_layersi_next_xi_next_ynext_layerstemp_layerswithCollisiongeoIndex)) == Integer.MIN_VALUE)
                return 
1;
            
curr_next_switcher curr_layers;
            
curr_layers next_layers;
            
next_layers curr_next_switcher;
            
curr_x i_next_x;
            
curr_y i_next_y;
        }
        
diff_z curr_z _tz;
        
dz Math.abs(diff_z);
        if (
Config.ALLOW_FALL_FROM_WALLS)
            return 
diff_z Config.MAX_Z_DIFF diff_z 10000;
        return 
dz Config.MAX_Z_DIFF dz 1000 0;
    }

    private static 
Location MoveCheck(int __xint __yint _zint __txint __tyboolean withCollisionboolean backwardMoveboolean returnPrevint geoIndex) {
        
int _x __x World.MAP_MIN_X >> 4;
        
int _y __y World.MAP_MIN_Y >> 4;
        
int _tx __tx World.MAP_MIN_X >> 4;
        
int _ty __ty World.MAP_MIN_Y >> 4;

        
int diff_x _tx _xdiff_y _ty _y;
        
int dx Math.abs(diff_x), dy Math.abs(diff_y);
        
float steps Math.max(dxdy);
        if (
steps == 0)
            return new 
Location(__x__y_z);

        
float step_x diff_x stepsstep_y diff_y steps;
        
int curr_x _xcurr_y _ycurr_z _z;
        
float next_x curr_xnext_y curr_y;
        
int i_next_xi_next_yi_next_z curr_z;

        
short[] next_layers = new short[MAX_LAYERS 1];
        
short[] temp_layers = new short[MAX_LAYERS 1];
        
short[] curr_layers = new short[MAX_LAYERS 1];
        
short[] curr_next_switcher;
        
NGetLayers(curr_xcurr_ycurr_layersgeoIndex);
        
int prev_x curr_xprev_y curr_yprev_z curr_z;

        for (
int i 0stepsi++) {
            
next_x += step_x;
            
next_y += step_y;
            
i_next_x = (int) (next_x 0.5f);
            
i_next_y = (int) (next_y 0.5f);
            
NGetLayers(i_next_xi_next_ynext_layersgeoIndex);
            if ((
i_next_z NcanMoveNext(curr_xcurr_ycurr_zcurr_layersi_next_xi_next_ynext_layerstemp_layerswithCollisiongeoIndex)) == Integer.MIN_VALUE)
                break;
            if (
backwardMove && NcanMoveNext(i_next_xi_next_yi_next_znext_layerscurr_xcurr_ycurr_layerstemp_layerswithCollisiongeoIndex) == Integer.MIN_VALUE)
                break;
            
curr_next_switcher curr_layers;
            
curr_layers next_layers;
            
next_layers curr_next_switcher;
            if (
returnPrev) {
                
prev_x curr_x;
                
prev_y curr_y;
                
prev_z curr_z;
            }
            
curr_x i_next_x;
            
curr_y i_next_y;
            
curr_z i_next_z;
        }

        if (
returnPrev) {
            
curr_x prev_x;
            
curr_y prev_y;
            
curr_z prev_z;
        }

        
//if(curr_x == _x && curr_y == _y)
        //    return new Location(__x, __y, _z);

        //log.info("move" + (backwardMove ? " back" : "") + (withCollision ? " +collision" : "") + ": " + curr_x + " " + curr_y + " " + curr_z + " / xyz: " + __x + " " + __y + " " + _z + " / to xy: " + __tx + " " + __ty + " / geo xy: " + _x + " " + _y + " / geo to xy: " + _tx + " " + _ty);
        
return new Location(curr_xcurr_ycurr_z).geo2world();
    }

    
/**
     * Аналогичен CanMove, но возвращает весь пройденный путь. В гео координатах.
     */
    
public static List<LocationMoveList(int __xint __yint _zint __txint __tyint geoIndexboolean onlyFullPath) {
        
int _x __x World.MAP_MIN_X >> 4;
        
int _y __y World.MAP_MIN_Y >> 4;
        
int _tx __tx World.MAP_MIN_X >> 4;
        
int _ty __ty World.MAP_MIN_Y >> 4;

        
int diff_x _tx _xdiff_y _ty _y;
        
int dx Math.abs(diff_x), dy Math.abs(diff_y);
        
double steps Math.max(dxdy);
        if (
steps == 0// Никуда не идем
            
return Collections.emptyList();

        
double step_x diff_x stepsstep_y diff_y steps;
        
int curr_x _xcurr_y _ycurr_z _z;
        
double next_x curr_xnext_y curr_y;
        
int i_next_xi_next_yi_next_z curr_z;

        
short[] next_layers = new short[MAX_LAYERS 1];
        
short[] temp_layers = new short[MAX_LAYERS 1];
        
short[] curr_layers = new short[MAX_LAYERS 1];
        
short[] curr_next_switcher;

        
NGetLayers(curr_xcurr_ycurr_layersgeoIndex);
        if (
curr_layers[0] == 0)
            return 
null;

        List<
Locationresult = new ArrayList<Location>((int) steps 1);

        
result.add(new Location(curr_xcurr_ycurr_z)); // Первая точка

        
steps Math.ceil(steps);
        for (
int i 0< (int) stepsi++) {
            
next_x += step_x;
            
next_y += step_y;
            
i_next_x = (int) (next_x 0.5);
            
i_next_y = (int) (next_y 0.5);

            
NGetLayers(i_next_xi_next_ynext_layersgeoIndex);
            if ((
i_next_z NcanMoveNext(curr_xcurr_ycurr_zcurr_layersi_next_xi_next_ynext_layerstemp_layersfalsegeoIndex)) == Integer.MIN_VALUE)
                if (
onlyFullPath)
                    return 
null;
                else
                    break;

            
curr_next_switcher curr_layers;
            
curr_layers next_layers;
            
next_layers curr_next_switcher;

            
curr_x i_next_x;
            
curr_y i_next_y;
            
curr_z i_next_z;

            
result.add(new Location(curr_xcurr_ycurr_z));
        }

        return 
result;
    }

    
/**
     * Используется только для антипаровоза в AI
     */
    
private static Location MoveCheckForAI(int xint yint zint txint tyint geoIndex) {
        
int dx tx x;
        
int dy ty y;
        
int inc_x sign(dx);
        
int inc_y sign(dy);
        
dx Math.abs(dx);
        
dy Math.abs(dy);
        if (
dx dy || dx == && dy == || dx == && dy == 2)
            return new 
Location(xyz).geo2world();
        
int prev_x x;
        
int prev_y y;
        
int prev_z z;
        
int next_x x;
        
int next_y y;
        
int next_z z;
        if (
dx >= dy// dy/dx <= 1
        
{
            
int delta_A dy;
            
int d delta_A dx;
            
int delta_B delta_A dx;
            for (
int i 0dxi++) {
                
prev_x x;
                
prev_y y;
                
prev_z z;
                
next_x;
                
next_y;
                
next_z;
                if (
0) {
                    
+= delta_B;
                    
next_x += inc_x;
                    
next_y += inc_y;
                } else {
                    
+= delta_A;
                    
next_x += inc_x;
                }
                
next_z NcanMoveNextForAI(xyznext_xnext_ygeoIndex);
                if (
next_z == 0)
                    return new 
Location(prev_xprev_yprev_z).geo2world();
            }
        } else {
            
int delta_A dx;
            
int d delta_A dy;
            
int delta_B delta_A dy;
            for (
int i 0dyi++) {
                
prev_x x;
                
prev_y y;
                
prev_z z;
                
next_x;
                
next_y;
                
next_z;
                if (
0) {
                    
+= delta_B;
                    
next_x += inc_x;
                    
next_y += inc_y;
                } else {
                    
+= delta_A;
                    
next_y += inc_y;
                }
                
next_z NcanMoveNextForAI(xyznext_xnext_ygeoIndex);
                if (
next_z == 0)
                    return new 
Location(prev_xprev_yprev_z).geo2world();
            }
        }
        return new 
Location(next_xnext_ynext_z).geo2world();
    }

    private static 
boolean NcanMoveNextExCheck(int xint yint hint nextxint nextyint hexthshort[] temp_layersint geoIndex) {
        
NGetLayers(xytemp_layersgeoIndex);
        if (
temp_layers[0] == 0)
            return 
true;

        
int temp_layer;
        if ((
temp_layer FindNearestLowerLayer(temp_layersConfig.MIN_LAYER_HEIGHT)) == Integer.MIN_VALUE)
            return 
false;
        
short temp_layer_h = (short) ((short) (temp_layer 0x0fff0) >> 1);
        if (
Math.abs(temp_layer_h hexth) >= Config.MAX_Z_DIFF || Math.abs(temp_layer_h h) >= Config.MAX_Z_DIFF)
            return 
false;
        return 
checkNSWE((byte) (temp_layer 0x0F), xynextxnexty);
    }

    
/**
     * @return возвращает высоту следующего блока, либо Integer.MIN_VALUE если двигатся нельзя
     */
    
public static int NcanMoveNext(int xint yint zshort[] layersint next_xint next_yshort[] next_layersshort[] temp_layersboolean withCollisionint geoIndex) {
        if (
layers[0] == || next_layers[0] == 0)
            return 
z;

        
int layernext_layer;
        if ((
layer FindNearestLowerLayer(layersConfig.MIN_LAYER_HEIGHT)) == Integer.MIN_VALUE)
            return 
Integer.MIN_VALUE;

        
byte layer_nswe = (byte) (layer 0x0F);
        if (!
checkNSWE(layer_nswexynext_xnext_y))
            return 
Integer.MIN_VALUE;

        
short layer_h = (short) ((short) (layer 0x0fff0) >> 1);
        if ((
next_layer FindNearestLowerLayer(next_layerslayer_h Config.MIN_LAYER_HEIGHT)) == Integer.MIN_VALUE)
            return 
Integer.MIN_VALUE;

        
short next_layer_h = (short) ((short) (next_layer 0x0fff0) >> 1);
        
/*if(withCollision && next_layer_h + Config.MAX_Z_DIFF < layer_h)
              return Integer.MIN_VALUE;*/

        // если движение не по диагонали
        
if (== next_x || == next_y) {
            if (
withCollision) {
                
//short[] heightNSWE = temp_layers;
                
if (== next_x) {
                    
NgetHeightAndNSWE(1ylayer_htemp_layersgeoIndex);
                    if (
Math.abs(temp_layers[0] - layer_h) > 15 || !checkNSWE(layer_nswe1yxy) || !checkNSWE((bytetemp_layers[1], 1y1next_y))
                        return 
Integer.MIN_VALUE;

                    
NgetHeightAndNSWE(1ylayer_htemp_layersgeoIndex);
                    if (
Math.abs(temp_layers[0] - layer_h) > 15 || !checkNSWE(layer_nswe1yxy) || !checkNSWE((bytetemp_layers[1], 1y1next_y))
                        return 
Integer.MIN_VALUE;

                    return 
next_layer_h;
                }

                
NgetHeightAndNSWE(x1layer_htemp_layersgeoIndex);
                if (
Math.abs(temp_layers[0] - layer_h) >= Config.MAX_Z_DIFF || !checkNSWE(layer_nswex1xy) || !checkNSWE((bytetemp_layers[1], x1next_x1))
                    return 
Integer.MIN_VALUE;

                
NgetHeightAndNSWE(x1layer_htemp_layersgeoIndex);
                if (
Math.abs(temp_layers[0] - layer_h) >= Config.MAX_Z_DIFF || !checkNSWE(layer_nswex1xy) || !checkNSWE((bytetemp_layers[1], x1next_x1))
                    return 
Integer.MIN_VALUE;
            }

            return 
next_layer_h;
        }

        if (!
NcanMoveNextExCheck(xnext_ylayer_hnext_xnext_ynext_layer_htemp_layersgeoIndex))
            return 
Integer.MIN_VALUE;
        if (!
NcanMoveNextExCheck(next_xylayer_hnext_xnext_ynext_layer_htemp_layersgeoIndex))
            return 
Integer.MIN_VALUE;

        
//FIXME if(withCollision)

        
return next_layer_h;
    }

    
/**
     * Используется только для антипаровоза в AI
     */
    
public static int NcanMoveNextForAI(int xint yint zint next_xint next_yint geoIndex) {
        
short[] layers1 = new short[MAX_LAYERS 1];
        
short[] layers2 = new short[MAX_LAYERS 1];
        
NGetLayers(xylayers1geoIndex);
        
NGetLayers(next_xnext_ylayers2geoIndex);

        if (
layers1[0] == || layers2[0] == 0)
            return 
== z;

        
short h;

        
short z1 Short.MIN_VALUE;
        
byte NSWE1 NSWE_ALL;
        for (
int i 1<= layers1[0]; i++) {
            
= (short) ((short) (layers1[i] & 0x0fff0) >> 1);
            if (
Math.abs(z1) > Math.abs(h)) {
                
z1 h;
                
NSWE1 = (byte) (layers1[i] & 0x0F);
            }
        }

        if (
z1 == Short.MIN_VALUE)
            return 
0;

        
short z2 Short.MIN_VALUE;
        
byte NSWE2 NSWE_ALL;
        for (
int i 1<= layers2[0]; i++) {
            
= (short) ((short) (layers2[i] & 0x0fff0) >> 1);
            if (
Math.abs(z2) > Math.abs(h)) {
                
z2 h;
                
NSWE2 = (byte) (layers2[i] & 0x0F);
            }
        }

        if (
z2 == Short.MIN_VALUE)
            return 
0;

        if (
z1 z2 && z1 z2 Config.MAX_Z_DIFF)
            return 
0;

        if (!
checkNSWE(NSWE1xynext_xnext_y) || !checkNSWE(NSWE2next_xnext_yxy))
            return 
0;

        return 
z2 == z2;
    }

    
/**
     * в нулевую ячейку кладется длина
     *
     * @param geoX
     * @param geoY
     * @param result
     */
    
public static void NGetLayers(int geoXint geoYshort[] resultint geoIndex) {
        
result[0] = 0;
        
byte[] block getGeoBlockFromGeoCoords(geoXgeoYgeoIndex);
        if (
block == null)
            return;

        
int cellXcellY;
        
int index 0;
        
// Read current block type: 0 - flat, 1 - complex, 2 - multilevel
        
byte type block[index];
        
index++;

        switch (
type) {
            case 
BLOCKTYPE_FLAT:
                
short height makeShort(block[index 1], block[index]);
                
height = (short) (height 0x0fff0);
                
result[0]++;
                
result[1] = (short) ((short) (height << 1) | NSWE_ALL);
                return;
            case 
BLOCKTYPE_COMPLEX:
                
cellX getCell(geoX);
                
cellY getCell(geoY);
                
index += (cellX << 3) + cellY << 1;
                
height makeShort(block[index 1], block[index]);
                
result[0]++;
                
result[1] = height;
                return;
            case 
BLOCKTYPE_MULTILEVEL:
                
cellX getCell(geoX);
                
cellY getCell(geoY);
                
int offset = (cellX << 3) + cellY;
                while (
offset 0) {
                    
byte lc block[index];
                    
index += (lc << 1) + 1;
                    
offset--;
                }
                
byte layer_count block[index];
                
index++;
                if (
layer_count <= || layer_count MAX_LAYERS)
                    return;
                
result[0] = layer_count;
                while (
layer_count 0) {
                    
result[layer_count] = makeShort(block[index 1], block[index]);
                    
layer_count--;
                    
index += 2;
                }
                return;
            default:
                
_log.error("GeoEngine: Unknown block type");
                return;
        }
    }

    private static 
short NgetType(int geoXint geoYint geoIndex) {
        
byte[] block getGeoBlockFromGeoCoords(geoXgeoYgeoIndex);

        if (
block == null)
            return 
0;

        return 
block[0];
    }

    public static 
int NgetHeight(int geoXint geoYint zint geoIndex) {
        
byte[] block getGeoBlockFromGeoCoords(geoXgeoYgeoIndex);

        if (
block == null)
            return 
z;

        
int cellXcellYindex 0;

        
// Read current block type: 0 - flat, 1 - complex, 2 - multilevel
        
byte type block[index];
        
index++;

        
short height;
        switch (
type) {
            case 
BLOCKTYPE_FLAT:
                
height makeShort(block[index 1], block[index]);
                return (
short) (height 0x0fff0);
            case 
BLOCKTYPE_COMPLEX:
                
cellX getCell(geoX);
                
cellY getCell(geoY);
                
index += (cellX << 3) + cellY << 1;
                
height makeShort(block[index 1], block[index]);
                return (
short) ((short) (height 0x0fff0) >> 1); // height / 2
            
case BLOCKTYPE_MULTILEVEL:
                
cellX getCell(geoX);
                
cellY getCell(geoY);
                
int offset = (cellX << 3) + cellY;
                while (
offset 0) {
                    
byte lc block[index];
                    
index += (lc << 1) + 1;
                    
offset--;
                }
                
byte layers block[index];
                
index++;
                if (
layers <= || layers MAX_LAYERS)
                    return (
shortz;

                
int z_nearest_lower_limit Config.MIN_LAYER_HEIGHT;
                
int z_nearest_lower Integer.MIN_VALUE;
                
int z_nearest Integer.MIN_VALUE;

                while (
layers 0) {
                    
height = (short) ((short) (makeShort(block[index 1], block[index]) & 0x0fff0) >> 1);
                    if (
height z_nearest_lower_limit)
                        
z_nearest_lower Math.max(z_nearest_lowerheight);
                    else if (
Math.abs(height) < Math.abs(z_nearest))
                        
z_nearest height;
                    
layers--;
                    
index += 2;
                }

                return 
z_nearest_lower != Integer.MIN_VALUE z_nearest_lower z_nearest;
            default:
                
_log.error("GeoEngine: Unknown blockType");
                return 
z;
        }
    }

    
/**
     * @param geoX позиция геодаты
     * @param geoY позиция геодаты
     * @param z    координата без изменений
     * @return NSWE: 0-15
     */
    
public static byte NgetNSWE(int geoXint geoYint zint geoIndex) {
        
byte[] block getGeoBlockFromGeoCoords(geoXgeoYgeoIndex);

        if (
block == null)
            return 
NSWE_ALL;

        
int cellXcellY;
        
int index 0;

        
// Read current block type: 0 - flat, 1 - complex, 2 - multilevel
        
byte type block[index];
        
index++;

        switch (
type) {
            case 
BLOCKTYPE_FLAT:
                return 
NSWE_ALL;
            case 
BLOCKTYPE_COMPLEX:
                
cellX getCell(geoX);
                
cellY getCell(geoY);
                
index += (cellX << 3) + cellY << 1;
                
short height makeShort(block[index 1], block[index]);
                return (
byte) (height 0x0F);
            case 
BLOCKTYPE_MULTILEVEL:
                
cellX getCell(geoX);
                
cellY getCell(geoY);
                
int offset = (cellX << 3) + cellY;
                while (
offset 0) {
                    
byte lc block[index];
                    
index += (lc << 1) + 1;
                    
offset--;
                }
                
byte layers block[index];
                
index++;
                if (
layers <= || layers MAX_LAYERS)
                    return 
NSWE_ALL;

                
short tempz1 Short.MIN_VALUE;
                
short tempz2 Short.MIN_VALUE;
                
int index_nswe1 NSWE_NONE;
                
int index_nswe2 NSWE_NONE;
                
int z_nearest_lower_limit Config.MIN_LAYER_HEIGHT;

                while (
layers 0) {
                    
height = (short) ((short) (makeShort(block[index 1], block[index]) & 0x0fff0) >> 1); // height / 2

                    
if (height z_nearest_lower_limit) {
                        if (
height tempz1) {
                            
tempz1 height;
                            
index_nswe1 index;
                        }
                    } else if (
Math.abs(height) < Math.abs(tempz2)) {
                        
tempz2 height;
                        
index_nswe2 index;
                    }

                    
layers--;
                    
index += 2;
                }

                if (
index_nswe1 0)
                    return (
byte) (makeShort(block[index_nswe1 1], block[index_nswe1]) & 0x0F);
                if (
index_nswe2 0)
                    return (
byte) (makeShort(block[index_nswe2 1], block[index_nswe2]) & 0x0F);

                return 
NSWE_ALL;
            default:
                
_log.error("GeoEngine: Unknown block type.");
                return 
NSWE_ALL;
        }
    }

    public static 
void NgetHeightAndNSWE(int geoXint geoYshort zshort[] resultint geoIndex) {
        
byte[] block getGeoBlockFromGeoCoords(geoXgeoYgeoIndex);

        if (
block == null) {
            
result[0] = z;
            
result[1] = NSWE_ALL;
            return;
        }

        
int cellXcellYindex 0;
        
short heightNSWE NSWE_ALL;

        
// Read current block type: 0 - flat, 1 - complex, 2 - multilevel
        
byte type block[index];
        
index++;

        switch (
type) {
            case 
BLOCKTYPE_FLAT:
                
height makeShort(block[index 1], block[index]);
                
result[0] = (short) (height 0x0fff0);
                
result[1] = NSWE_ALL;
                return;
            case 
BLOCKTYPE_COMPLEX:
                
cellX getCell(geoX);
                
cellY getCell(geoY);
                
index += (cellX << 3) + cellY << 1;
                
height makeShort(block[index 1], block[index]);
                
result[0] = (short) ((short) (height 0x0fff0) >> 1); // height / 2
                
result[1] = (short) (height 0x0F);
                return;
            case 
BLOCKTYPE_MULTILEVEL:
                
cellX getCell(geoX);
                
cellY getCell(geoY);
                
int offset = (cellX << 3) + cellY;
                while (
offset 0) {
                    
byte lc block[index];
                    
index += (lc << 1) + 1;
                    
offset--;
                }
                
byte layers block[index];
                
index++;
                if (
layers <= || layers MAX_LAYERS) {
                    
result[0] = z;
                    
result[1] = NSWE_ALL;
                    return;
                }

                
short tempz1 Short.MIN_VALUE;
                
short tempz2 Short.MIN_VALUE;
                
int index_nswe1 0;
                
int index_nswe2 0;
                
int z_nearest_lower_limit Config.MIN_LAYER_HEIGHT;

                while (
layers 0) {
                    
height = (short) ((short) (makeShort(block[index 1], block[index]) & 0x0fff0) >> 1); // height / 2

                    
if (height z_nearest_lower_limit) {
                        if (
height tempz1) {
                            
tempz1 height;
                            
index_nswe1 index;
                        }
                    } else if (
Math.abs(height) < Math.abs(tempz2)) {
                        
tempz2 height;
                        
index_nswe2 index;
                    }

                    
layers--;
                    
index += 2;
                }

                if (
index_nswe1 0) {
                    
NSWE makeShort(block[index_nswe1 1], block[index_nswe1]);
                    
NSWE = (short) (NSWE 0x0F);
                } else if (
index_nswe2 0) {
                    
NSWE makeShort(block[index_nswe2 1], block[index_nswe2]);
                    
NSWE = (short) (NSWE 0x0F);
                }
                
result[0] = tempz1 Short.MIN_VALUE tempz1 tempz2;
                
result[1] = NSWE;
                return;
            default:
                
_log.error("GeoEngine: Unknown block type.");
                
result[0] = z;
                
result[1] = NSWE_ALL;
                return;
        }
    }

    protected static 
short makeShort(byte b1byte b0) {
        return (
short) (b1 << b0 0xff);
    }

    
/**
     * @param geoPos позиция геодаты
     * @return Block Index: 0-255
     */
    
protected static int getBlock(int geoPos) {
        return (
geoPos >> 3) % 256;
    }

    
/**
     * @param geoPos позиция геодаты
     * @return Cell Index: 0-7
     */
    
protected static int getCell(int geoPos) {
        return 
geoPos 8;
    }

    
/**
     * Создает индекс блока геодаты по заданым координатам блока.
     *
     * @param blockX блок по geoX
     * @param blockY блок по geoY
     * @return индекс блока
     */
    
protected static int getBlockIndex(int blockXint blockY)
    {
        return (
blockX << 8) + blockY;
    }

    private static 
byte sign(int x) {
        if (
>= 0)
            return +
1;
        return -
1;
    }

    
/**
     * Возвращает актуальный блок для текущих геокоординат.<BR>
     * Является заготовкой для возвращения отдельніх блоков с дверьми
     *
     * @param geoX геокоордината
     * @param geoY геокоордината
     * @return текущий блок геодаты, или null если нет геодаты.
     */
    
private static byte[] getGeoBlockFromGeoCoords(int geoXint geoYint geoIndex) {
        if (!
Config.ALLOW_GEODATA)
            return 
null;

        
int ix geoX >> 10;
        
int iy geoY >> 10;

        if(
ix || ix >= World.WORLD_SIZE_X || iy || iy >= World.WORLD_SIZE_Y)
            return 
null;

        
byte[][][] region geodata[ix][iy];

        if(
region == null)
            return 
null;

        
int blockX getBlock(geoX);
        
int blockY getBlock(geoY);

        if(
region.length <= geoIndex)
            
geoIndex 0;

        return 
region[getBlockIndex(blockXblockY)][geoIndex];
    }

    
/**
     * Загрузка геодаты в память
     */
    
public static void load() {
        if (!
Config.ALLOW_GEODATA)
            return;

        
_log.info("GeoEngine: Loading Geodata...");

        
File f = new File(Config.DATAPACK_ROOT"geodata");

        if (!
f.exists() || !f.isDirectory()) {
            
_log.info("GeoEngine: Files missing, loading aborted.");

            return;
        }

        
int counter 0;
        
Pattern p Pattern.compile(Config.GEOFILES_PATTERN);

        for (
File q f.listFiles()) {
            if (
q.isDirectory())
                continue;

            
String fn q.getName();
            
Matcher m p.matcher(fn);
            if (
m.matches()) {
                
fn fn.substring(05); // обрезаем .l2j
                
String[] xy fn.split("_");
                
byte rx Byte.parseByte(xy[0]);
                
byte ry Byte.parseByte(xy[1]);

                
LoadGeodataFile(rxry);
                
LoadGeodata(rxry0);

                
counter++;
            }
        }

        
_log.info("GeoEngine: Loaded " counter " map(s), max layers: " MAX_LAYERS);

        if (
Config.COMPACT_GEO)
            
compact();
    }

    public static 
void DumpGeodata(String dir) {
        new 
File(dir).mkdirs();
        for (
int mapX 0mapX World.WORLD_SIZE_XmapX++)
            for (
int mapY 0mapY World.WORLD_SIZE_YmapY++) {
                if (
geodata[mapX][mapY] == null)
                    continue;
                
int rx mapX Config.GEO_X_FIRST;
                
int ry mapY Config.GEO_Y_FIRST;
                
String fName dir "/" rx "_" ry ".l2j";
                
_log.info("Dumping geo: " fName);
                
DumpGeodataFile(fName, (byterx, (bytery);
            }
    }

    public static 
boolean DumpGeodataFile(int cxint cy) {
        return 
DumpGeodataFileMap((byte) (Math.floor((float) cx / (float) 32768) + 20), (byte) (Math.floor((float) cy / (float) 32768) + 18));
    }

    public static 
boolean DumpGeodataFileMap(byte rxbyte ry) {
        
String name "log/" rx "_" ry ".l2j";
        return 
DumpGeodataFile(namerxry);
    }

    public static 
boolean DumpGeodataFile(String namebyte rxbyte ry) {
        
int ix rx Config.GEO_X_FIRST;
        
int iy ry Config.GEO_Y_FIRST;

        
byte[][] geoblocks geodata[ix][iy][0];
        if (
geoblocks == null)
            return 
false;

        
File f = new File(name);
        if (
f.exists())
            
f.delete();
        
OutputStream os null;
        try {
            
os = new BufferedOutputStream(new FileOutputStream(f));
            for (
byte[] geoblock geoblocks)
                
os.write(geoblock);
        } catch (
IOException e) {
            
_log.error(""e);
            return 
false;
        } 
finally {
            if (
os != null)
                try {
                    
os.close();
                } catch (
Exception e) {

                }
        }

        return 
true;
    }

    
/**
     * Загрузка региона геодаты.
     *
     * @param rx регион x
     * @param ry регион y
     */
    
public static boolean LoadGeodataFile(byte rxbyte ry) {
        
String fname "geodata/" rx "_" ry ".l2j";
        
int ix rx Config.GEO_X_FIRST;
        
int iy ry Config.GEO_Y_FIRST;

        if (
ix || iy || ix > (World.MAP_MAX_X >> 15) + Math.abs(World.MAP_MIN_X >> 15) || iy > (World.MAP_MAX_Y >> 15) + Math.abs(World.MAP_MIN_Y >> 15)) {
            
_log.info("GeoEngine: File " fname " was not loaded!!! ");
            return 
false;
        }

        
_log.info("GeoEngine: Loading: " fname);

        
File geoFile = new File(Config.DATAPACK_ROOTfname);

        try {
            
FileChannel roChannel = new RandomAccessFile(geoFile"r").getChannel();
            
long size roChannel.size();
            
MappedByteBuffer buf roChannel.map(FileChannel.MapMode.READ_ONLY0size);
            
buf.order(ByteOrder.LITTLE_ENDIAN);
            
rawgeo[ix][iy] = buf;

            if (
size BLOCKS_IN_MAP 3)
                throw new 
RuntimeException("Invalid geodata : " fname "!");

            return 
true;
        } catch (
IOException e) {
            
_log.error(""e);
        }

        return 
false;
    }

    public static 
boolean LoadGeodata(int rxint ryint regIndex) {
        
String fname "./geodata/" rx "_" ry ".l2j";
        
int ix rx Config.GEO_X_FIRST;
        
int iy ry Config.GEO_Y_FIRST;

        if(
ix || iy || ix > (World.MAP_MAX_X >> 15) + Math.abs(World.MAP_MIN_X >> 15) || iy > (World.MAP_MAX_Y >> 15) + Math.abs(World.MAP_MIN_Y >> 15))
        {
            
_log.info("Geo Engine: File " fname " was not loaded!!! ");
            return 
false;
        }

        
//log.info("Geo Engine: - Loading: " + fname);

        
File Geo = new File(fname);
        
int sizeindex 0block 0flor 0;
        try
        {
            
byte[] geo;
            
synchronized (geodata)
            {
                
// Create a read-only memory-mapped file
                
FileChannel roChannel = new RandomAccessFile(Geo"r").getChannel();
                
size = (int) roChannel.size();
                
ByteBuffer buffer roChannel.map(FileChannel.MapMode.READ_ONLY0size);
                
roChannel.close();
                
buffer.order(ByteOrder.LITTLE_ENDIAN);
                
geo = new byte[buffer.remaining()];
                
buffer.get(geo0geo.length);
            }
            if(
size >= BLOCKS_IN_MAP 3)
            {
                
byte[][][] blocks = new byte[BLOCKS_IN_MAP][1][]; // 256 * 256 блоков в регионе геодаты

                // Indexing geo files, so we will know where each block starts
                
for(block 0block blocks.lengthblock++)
                {
                    
byte type geo[index];
                    
index++;

                    
byte[] geoBlock;
                    switch(
type)
                    {
                        case 
BLOCKTYPE_FLAT:

                            
// Создаем блок геодаты
                            
geoBlock = new byte[1];

                            
// Читаем нужные даные с геодаты
                            
geoBlock[0] = type;
                            
geoBlock[1] = geo[index];
                            
geoBlock[2] = geo[index 1];

                            
// Увеличиваем индекс
                            
index += 2;

                            
// Добавляем блок геодаты
                            
blocks[block][0] = geoBlock;
                            break;

                        case 
BLOCKTYPE_COMPLEX:

                            
// Создаем блок геодаты
                            
geoBlock = new byte[128 1];

                            
// Читаем даные с геодаты
                            
geoBlock[0] = type;
                            
System.arraycopy(geoindexgeoBlock1128);

                            
// Увеличиваем индекс
                            
index += 128;

                            
// Добавляем блок геодаты
                            
blocks[block][0] = geoBlock;
                            break;

                        case 
BLOCKTYPE_MULTILEVEL:
                            
// Оригинальный индекс
                            
int orgIndex index;

                            
// Считаем длину блока геодаты
                            
for(int b 064b++)
                            {
                                
byte layers geo[index];
                                
MAX_LAYERS Math.max(MAX_LAYERSlayers);
                                
index += (layers << 1) + 1;
                                if(
layers flor)
                                    
flor layers;
                            }

                            
// Получаем длину
                            
int diff index orgIndex;

                            
// Создаем массив геодаты
                            
geoBlock = new byte[diff 1];

                            
// Читаем даные с геодаты
                            
geoBlock[0] = type;
                            
System.arraycopy(geoorgIndexgeoBlock1diff);

                            
// Добавляем блок геодаты
                            
blocks[block][0] = geoBlock;
                            break;
                        default:
                            
_log.info("GeoEngine: invalid block type: " type);
                    }
                }

                
synchronized (geodata)
                {
                    
geodata[ix][iy] = blocks;
                }
                return 
true;
            }
            return 
false;
        }
        catch(
Exception e)
        {
            
e.printStackTrace();
            
_log.warn("Failed to Load GeoFile at block: " block "\n");
            return 
false;
        }
    }

    public static 
int NextGeoIndex(int rxint ryint refId) {
        if (!
Config.ALLOW_GEODATA)
            return 
0;

        
int ix rx Config.GEO_X_FIRST;
        
int iy ry Config.GEO_Y_FIRST;

        
int regIndex = -1;

        
synchronized (geodata) {
            
byte[][][] region geodata[ix][iy];

            
//Ищем свободный блок
            
for (int i 0region.lengthi++) {
                if (
region[i] == null) {
                    
regIndex i;
                    break;
                }
            }

            
//Свободного блока нет, создаем новый
            
if (regIndex == -1) {
                
byte[][][] resizedRegion = new byte[(regIndex region.length) + 1][][];
                for (
int i 0region.lengthi++)
                    
resizedRegion[i] = region[i];
                
geodata[ix][iy] = resizedRegion;
            }

            
LoadGeodata(rxryregIndex);
        }

        return 
0x0f000000 | (ix << 16) | (iy << 8) | regIndex;
    }

    
/**
     * Освободить занятый рефлектом индекс геодаты.
     *
     * @param geoIndex
     */
    
public static void FreeGeoIndex(int geoIndex) {
        if (!
Config.ALLOW_GEODATA)
            return;

        
//Рефлект без геодаты
        
if ((geoIndex 0x0f000000) != 0x0f000000)
            return;

        
int ix = (geoIndex 0x00ff0000) >> 16;
        
int iy = (geoIndex 0x0000ff00) >> 8;
        
int regIndex geoIndex 0x000000ff;

        
synchronized (geodata) {
            
geodata[ix][iy][regIndex] = null;
        }
    }

    
/* NOT USED
     private static void copyBlock(int ix, int iy, int blockIndex)
     {
         byte[][][] region = geodata[ix][iy];

         if(region == null)
         {
             Log.add("door at null region? [" + ix + "][" + iy + "]", "doors");
             return;
         }

         byte[] block = region[blockIndex][0];
         byte blockType = block[0];

         switch(blockType)
         {
             case BLOCKTYPE_FLAT:
                 short height = makeShort(block[2], block[1]);
                 height &= 0x0fff0;
                 height <<= 1;
                 height |= NORTH;
                 height |= SOUTH;
                 height |= WEST;
                 height |= EAST;
                 byte[] newblock = new byte[129];
                 newblock[0] = BLOCKTYPE_COMPLEX;
                 for(int i = 1; i < 129; i += 2)
                 {
                     newblock[i + 1] = (byte) (height >> 8);
                     newblock[i] = (byte) (height & 0x00ff);
                 }
                 region[blockIndex][0] = newblock;
                 break;
             default:
                 if(Config.COMPACT_GEO)
                     region[blockIndex][0] = region[blockIndex][0].clone();
                 break;
         }
     }
     */
    
public static void removeGeoCollision(GeoCollision collisionint geoIndex) {
        
Shape shape collision.getShape();

        
byte[][] around collision.getGeoAround();
        if (
around == null)
            throw new 
RuntimeException("Attempt to remove unitialized collision: " collision);

        
//Размер коллизии в клетках геодаты
        
int minX shape.getXmin() - World.MAP_MIN_X 16 >> 4;
        
int minY shape.getYmin() - World.MAP_MIN_Y 16 >> 4;
        
int minZ shape.getZmin();
        
int maxZ shape.getZmax();

        
short height;
        
byte old_nswe;

        for (
int gX 0gX around.lengthgX++)
            for (
int gY 0gY around[gX].lengthgY++) {
                
int geoX minX gX;
                
int geoY minY gY;

                
byte[] block getGeoBlockFromGeoCoords(geoXgeoYgeoIndex);
                if (
block == null)
                    continue;

                
int cellX getCell(geoX);
                
int cellY getCell(geoY);

                
int index 0;
                
byte blockType block[index];
                
index++;

                switch (
blockType) {
                    case 
BLOCKTYPE_COMPLEX:
                        
index += (cellX << 3) + cellY << 1;

                        
// Получаем высоту клетки
                        
height makeShort(block[index 1], block[index]);
                        
old_nswe = (byte) (height 0x0F);
                        
height &= 0xfff0;
                        
height >>= 1;

                        
// подходящий слой не найден
                        
if (height minZ || height maxZ)
                            break;

                        
// around
                        
height <<= 1;
                        
height &= 0xfff0;
                        
height |= old_nswe;
                        if (
collision.isConcrete())
                            
height |= around[gX][gY];
                        else
                            
height &= ~around[gX][gY];

                        
// Записываем высоту в массив
                        
block[index 1] = (byte) (height >> 8);
                        
block[index] = (byte) (height 0x00ff);
                        break;
                    case 
BLOCKTYPE_MULTILEVEL:
                        
// Последний валидный индекс для двери
                        
int neededIndex = -1;

                        
// Далее следует стандартный механизм получения высоты
                        
int offset = (cellX << 3) + cellY;
                        while (
offset 0) {
                            
byte lc block[index];
                            
index += (lc << 1) + 1;
                            
offset--;
                        }
                        
byte layers block[index];
                        
index++;
                        if (
layers <= || layers MAX_LAYERS)
                            break;
                        
short temph Short.MIN_VALUE;
                        
old_nswe NSWE_ALL;
                        while (
layers 0) {
                            
height makeShort(block[index 1], block[index]);
                            
byte tmp_nswe = (byte) (height 0x0F);
                            
height &= 0xfff0;
                            
height >>= 1;
                            
int z_diff_last Math.abs(minZ temph);
                            
int z_diff_curr Math.abs(maxZ height);
                            if (
z_diff_last z_diff_curr) {
                                
old_nswe tmp_nswe;
                                
temph height;
                                
neededIndex index;
                            }
                            
layers--;
                            
index += 2;
                        }

                        
// подходящий слой не найден
                        
if (temph == Short.MIN_VALUE || (temph minZ || temph maxZ))
                            break;

                        
// around
                        
temph <<= 1;
                        
temph &= 0xfff0;
                        
temph |= old_nswe;
                        if (
collision.isConcrete())
                            
temph |= around[gX][gY];
                        else
                            
temph &= ~around[gX][gY];

                        
// записываем высоту
                        
block[neededIndex 1] = (byte) (temph >> 8);
                        
block[neededIndex] = (byte) (temph 0x00ff);
                        break;
                }
            }
    }

    public static 
void applyGeoCollision(GeoCollision collisionint geoIndex) {
        
Shape shape collision.getShape();
        if (
shape.getXmax() == shape.getYmax() && shape.getXmax() == 0)
            throw new 
RuntimeException("Attempt to add incorrect collision: " collision);

        
boolean isFirstTime false;

        
//Размер коллизии в клетках геодаты
        
int minX shape.getXmin() - World.MAP_MIN_X 16 >> 4;
        
int maxX shape.getXmax() - World.MAP_MIN_X 16 >> 4;
        
int minY shape.getYmin() - World.MAP_MIN_Y 16 >> 4;
        
int maxY shape.getYmax() - World.MAP_MIN_Y 16 >> 4;
        
int minZ shape.getZmin();
        
int maxZ shape.getZmax();

        
byte[][] around collision.getGeoAround();
        if (
around == null) {
            
isFirstTime true;

            
//Сформируем коллизию
            
byte[][] cells = new byte[maxX minX 1][maxY minY 1];
            for (
int gX minXgX <= maxXgX++)
                for (
int gY minYgY <= maxYgY++) {
                    
int x = (gX << 4) + World.MAP_MIN_X;
                    
int y = (gY << 4) + World.MAP_MIN_Y;

                    
loop:
                    for (
int ax xax 16ax++)
                        for (
int ay yay 16ay++)
                            if (
shape.isInside(axay)) {
                                
cells[gX minX][gY minY] = 1;
                                break 
loop;
                            }
                }

            
around = new byte[maxX minX 1][maxY minY 1];
            for (
int gX 0gX cells.lengthgX++)
                for (
int gY 0gY cells[gX].lengthgY++) {
                    if (
cells[gX][gY] == 1) {
                        
around[gX][gY] = NSWE_ALL;

                        
byte _nswe;
                        if (
gY 0)
                            if (
cells[gX][gY 1] == 0) {
                                
_nswe around[gX][gY 1];
                                
_nswe |= SOUTH;
                                
around[gX][gY 1] = _nswe;
                            }
                        if (
gY cells[gX].length)
                            if (
cells[gX][gY 1] == 0) {
                                
_nswe around[gX][gY 1];
                                
_nswe |= NORTH;
                                
around[gX][gY 1] = _nswe;
                            }
                        if (
gX 0)
                            if (
cells[gX 1][gY] == 0) {
                                
_nswe around[gX 1][gY];
                                
_nswe |= EAST;
                                
around[gX 1][gY] = _nswe;
                            }
                        if (
gX cells.length)
                            if (
cells[gX 1][gY] == 0) {
                                
_nswe around[gX 1][gY];
                                
_nswe |= WEST;
                                
around[gX 1][gY] = _nswe;
                            }
                    }
                }

            
collision.setGeoAround(around);
        }

        
short height;
        
byte old_nsweclose_nswe;

        for (
int gX 0gX around.lengthgX++)
            for (
int gY 0gY around[gX].lengthgY++) {
                
int geoX minX gX;
                
int geoY minY gY;

                
// Попытка скопировать блок геодаты, если уже существует, то не скопируется
                //TODO: if(first_time)
                //    copyBlock(ix, iy, blockIndex);

                
byte[] block getGeoBlockFromGeoCoords(geoXgeoYgeoIndex);
                if (
block == null)
                    continue;

                
int cellX getCell(geoX);
                
int cellY getCell(geoY);

                
int index 0;
                
byte blockType block[index];
                
index++;

                switch (
blockType) {
                    case 
BLOCKTYPE_COMPLEX:
                        
index += (cellX << 3) + cellY << 1;

                        
// Получаем высоту клетки
                        
height makeShort(block[index 1], block[index]);
                        
old_nswe = (byte) (height 0x0F);
                        
height &= 0xfff0;
                        
height >>= 1;

                        
// подходящий слой не найден
                        
if (height minZ || height maxZ)
                            break;

                        
close_nswe around[gX][gY];

                        if (
isFirstTime) {
                            if (
collision.isConcrete())
                                
close_nswe &= old_nswe;
                            else
                                
close_nswe &= ~old_nswe;
                            
around[gX][gY] = close_nswe;
                        }

                        
// around
                        
height <<= 1;
                        
height &= 0xfff0;
                        
height |= old_nswe;

                        if (
collision.isConcrete())
                            
height &= ~close_nswe;
                        else
                            
height |= close_nswe;

                        
// Записываем высоту в массив
                        
block[index 1] = (byte) (height >> 8);
                        
block[index] = (byte) (height 0x00ff);
                        break;
                    case 
BLOCKTYPE_MULTILEVEL:
                        
// Последний валидный индекс для двери
                        
int neededIndex = -1;

                        
// Далее следует стандартный механизм получения высоты
                        
int offset = (cellX << 3) + cellY;
                        while (
offset 0) {
                            
byte lc block[index];
                            
index += (lc << 1) + 1;
                            
offset--;
                        }
                        
byte layers block[index];
                        
index++;
                        if (
layers <= || layers MAX_LAYERS)
                            break;
                        
short temph Short.MIN_VALUE;
                        
old_nswe NSWE_ALL;
                        while (
layers 0) {
                            
height makeShort(block[index 1], block[index]);
                            
byte tmp_nswe = (byte) (height 0x0F);
                            
height &= 0xfff0;
                            
height >>= 1;
                            
int z_diff_last Math.abs(minZ temph);
                            
int z_diff_curr Math.abs(maxZ height);
                            if (
z_diff_last z_diff_curr) {
                                
old_nswe tmp_nswe;
                                
temph height;
                                
neededIndex index;
                            }
                            
layers--;
                            
index += 2;
                        }

                        
// подходящий слой не найден
                        
if (temph == Short.MIN_VALUE || (temph minZ || temph maxZ))
                            break;

                        
close_nswe around[gX][gY];

                        if (
isFirstTime) {
                            if (
collision.isConcrete())
                                
close_nswe &= old_nswe;
                            else
                                
close_nswe &= ~old_nswe;
                            
around[gX][gY] = close_nswe;
                        }

                        
// around
                        
temph <<= 1;
                        
temph &= 0xfff0;
                        
temph |= old_nswe;
                        if (
collision.isConcrete())
                            
temph &= ~close_nswe;
                        else
                            
temph |= close_nswe;

                        
// записываем высоту
                        
block[neededIndex 1] = (byte) (temph >> 8);
                        
block[neededIndex] = (byte) (temph 0x00ff);
                        break;
                }
            }
    }

    
/**
     * загружает заранее сгенерированые карты соовпадений в блоках и благодаря им оптимизирует размещение геодаты в памяти
     *
     * @return количество оптимизированых блоков
     */
    
public static void compact() {
        
long total 0optimized 0;
        
BlockLink[] links;
        
byte[][][] link_region;

        for (
int mapX 0mapX World.WORLD_SIZE_XmapX++)
            for (
int mapY 0mapY World.WORLD_SIZE_YmapY++) {
                if (
geodata[mapX][mapY] == null)
                    continue;
                
total += BLOCKS_IN_MAP;
                
links GeoOptimizer.loadBlockMatches("geodata/matches/" + (mapX Config.GEO_X_FIRST) + "_" + (mapY Config.GEO_Y_FIRST) + ".matches");
                if (
links == null)
                    continue;
                for (
int i 0links.lengthi++) {
                    
link_region geodata[links[i].linkMapX][links[i].linkMapY];
                    if (
link_region == null)
                        continue;
                    
link_region[links[i].linkBlockIndex][0] = geodata[mapX][mapY][links[i].blockIndex][0];
                    
optimized++;
                }
            }

        
_log.info(String.format("GeoEngine: - Compacted %d of %d blocks..."optimizedtotal));
    }

    
/**
     * сравнение двух байт-массивов
     *
     * @param a1
     * @param a2
     * @return
     */
    
public static boolean equalsData(byte[] a1byte[] a2) {
        if (
a1.length != a2.length)
            return 
false;
        for (
int i 0a1.lengthi++)
            if (
a1[i] != a2[i])
                return 
false;
        return 
true;
    }

    
/**
     * сравнение двух блоков геодаты
     *
     * @param mapX1
     * @param mapY1
     * @param blockIndex1
     * @param mapX2
     * @param mapY2
     * @param blockIndex2
     * @return
     */
    
public static boolean compareGeoBlocks(int mapX1int mapY1int blockIndex1int mapX2int mapY2int blockIndex2) {
        return 
equalsData(geodata[mapX1][mapY1][blockIndex1][0], geodata[mapX2][mapY2][blockIndex2][0]);
    }

    private static 
void initChecksums() {
        
_log.info("GeoEngine: - Generating Checksums...");
        new 
File(Config.DATAPACK_ROOT"geodata/checksum").mkdirs();
        
ExecutorService executor Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        
GeoOptimizer.checkSums = new int[World.WORLD_SIZE_X][World.WORLD_SIZE_Y][];
        for (
int mapX 0mapX World.WORLD_SIZE_XmapX++)
            for (
int mapY 0mapY World.WORLD_SIZE_YmapY++)
                if (
geodata[mapX][mapY] != null)
                    
executor.execute(new GeoOptimizer.CheckSumLoader(mapXmapYgeodata[mapX][mapY]));
        try {
            
executor.awaitTermination(Long.MAX_VALUETimeUnit.SECONDS);
        } catch (
InterruptedException e) {
            
_log.error(""e);
        }
    }

    private static 
void initBlockMatches(int maxScanRegions) {
        
_log.info("GeoEngine: Generating Block Matches...");
        new 
File(Config.DATAPACK_ROOT"geodata/matches").mkdirs();
        
ExecutorService executor Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        for (
int mapX 0mapX World.WORLD_SIZE_XmapX++)
            for (
int mapY 0mapY World.WORLD_SIZE_YmapY++)
                if (
geodata[mapX][mapY] != null && GeoOptimizer.checkSums != null && GeoOptimizer.checkSums[mapX][mapY] != null)
                    
executor.execute(new GeoOptimizer.GeoBlocksMatchFinder(mapXmapYmaxScanRegions));
        try {
            
executor.awaitTermination(Long.MAX_VALUETimeUnit.SECONDS);
        } catch (
InterruptedException e) {
            
_log.error(""e);
        }
    }

    public static 
void deleteChecksumFiles() {
        for (
int mapX 0mapX World.WORLD_SIZE_XmapX++)
            for (
int mapY 0mapY World.WORLD_SIZE_YmapY++) {
                if (
geodata[mapX][mapY] == null)
                    continue;
                new 
File(Config.DATAPACK_ROOT"geodata/checksum/" + (mapX Config.GEO_X_FIRST) + "_" + (mapY Config.GEO_Y_FIRST) + ".crc").delete();
            }
    }

    public static 
void genBlockMatches(int maxScanRegions) {
        
initChecksums();
        
initBlockMatches(maxScanRegions);
    }

    public static 
void unload() {
        for (
int mapX 0mapX World.WORLD_SIZE_XmapX++)
            for (
int mapY 0mapY World.WORLD_SIZE_YmapY++)
                
geodata[mapX][mapY] = null;
    }



ANZO 10.03.2012 03:34

Re: Работа над Goddess of Destruction (part 5)
 
Мде, все что поменять надо было это

Цитата:

World.MAP_MIN_Y
World.MAP_MAX_Y
World.MAP_MIN_X
World.MAP_MAX_X
:cw2:

Darvin 10.03.2012 12:22

Re: Работа над Goddess of Destruction (part 5)
 
нет там немного по другому я сделал. мне кажеться так более лучше получилось

darkevil 10.03.2012 12:50

Re: Работа над Goddess of Destruction (part 5)
 
Лезть в двиг геодаты не понимая как вся эта ахинея работает все равно что лезть в двигатель самолета ни разу не видев его в глаза, 1н косяк и сотни пассажиров в лепешку.

apocalipce 10.03.2012 19:16

Re: Работа над Goddess of Destruction (part 5)
 
Кто-нибудь есть система подъема и magmeld Cruma? У меня проблемы с работы
Lift - elevator ?

ALF. 14.03.2012 16:58

Re: Работа над Goddess of Destruction (part 5)
 
Ребят скиньте кто то плиз последнюю папку систем с NA-off (с Harmony-update)

[STIGMATED] 14.03.2012 17:01

Re: Работа над Goddess of Destruction (part 5)
 
ALFOS, http://narod.ru/disk/43765225001.5d1...ystem.zip.html

А мне бы с руоффа последний патч.

Darvin 17.03.2012 23:31

Re: Работа над Goddess of Destruction (part 5)
 
ребят, подскажите плиз что за
writeD(0x00);// Unknown GOD
writeD(0x00);// Unknown GOD
writeC(0x00);// Unknown GOD

в пакете UserInfo после writeD(_abnormalEffect2);

PSIFAK 22.03.2012 06:55

Re: Работа над Goddess of Destruction (part 5)
 
Поделитесь последним системом с руоффа и подскажите какой сейчас там протокол


Текущее время: 13:07. Часовой пояс GMT +3.

Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot