Beispiel #1
0
        public static BoxedData ValidateMovement(List <GeoDataElement> geoDataList, CharacterMovementDetails movementDetails)
        {
            BoxedData data = new BoxedData();
            GeoDataValidationDetails details = new GeoDataValidationDetails();
            string msg = "";

            try
            {
                #region Sub arrays
                //SUB ARRAYS
                GeoDataElement[] terrainSubArray  = new GeoDataElement[geoDataList.Count];
                GeoDataElement[] obstacleSubArray = new GeoDataElement[geoDataList.Count];
                int terrainFilledSize             = 0;
                int obstacleFilledSize            = 0;

                for (int i = 0; i < geoDataList.Count; i++)
                {
                    switch (geoDataList[i].ElementType)
                    {
                    case GeoDataElement.Type.Terrain:
                        terrainSubArray[terrainFilledSize] = geoDataList[i];
                        terrainFilledSize++;
                        break;

                    case GeoDataElement.Type.Obstacle:
                        obstacleSubArray[obstacleFilledSize] = geoDataList[i];
                        obstacleFilledSize++;
                        break;

                    case GeoDataElement.Type.Platform:
                        terrainSubArray[terrainFilledSize]   = geoDataList[i];
                        obstacleSubArray[obstacleFilledSize] = geoDataList[i];
                        terrainFilledSize++;
                        obstacleFilledSize++;
                        break;
                    }
                }

                #endregion

                //PARAMETERS

                bool            valid = true;
                Point3 <double> lastValidMovementPoint = movementDetails.OldLocationLocal.Copy();
                double          tStep = Measure.GetTParamStep(movementDetails.OldLocationLocal, movementDetails.NewLocationLocal)
                                        / 2;

                double tParam        = 0;
                double tParamClamped = 0;
                bool   tParamReached = false;

                //VALIDATION

                if (tStep > 0 && tStep < 1)
                {
                    double currentXDbl, currentYDbl, currentZDbl;
                    int    currentX, currentY, currentZ;
                    int    collisionMinX, collisionMaxX, collisionMinY, collisionMaxY, collisionMinZ, collisionMaxZ;

                    GeoDataElement geoDataElement;
                    bool           obstacleDetected = false;
                    bool           hasGroundUnderFeet;

                    while (!tParamReached)
                    {
                        tParamClamped = Measure.Clamp(0.0000, 1.0000, tParam);
                        currentXDbl   = Measure.Lerp(movementDetails.OldLocationLocal.X, movementDetails.NewLocationLocal.X, tParamClamped);
                        currentYDbl   = Measure.Lerp(movementDetails.OldLocationLocal.Y, movementDetails.NewLocationLocal.Y, tParamClamped);
                        currentZDbl   = Measure.Lerp(movementDetails.OldLocationLocal.Z, movementDetails.NewLocationLocal.Z, tParamClamped)
                                        + _marginZ;

                        currentX = Convert.ToInt32(Math.Floor(currentXDbl));
                        currentY = Convert.ToInt32(Math.Floor(currentYDbl));
                        currentZ = Convert.ToInt32(Math.Floor(currentZDbl));

                        #region Obstacle check
                        //OBSTACLE CHECK
                        for (int i = 0; i < obstacleFilledSize; i++)
                        {
                            geoDataElement = obstacleSubArray[i];
                            GetColliderPosition(geoDataElement, out collisionMinX, out collisionMaxX, out collisionMinY, out collisionMaxY, out collisionMinZ, out collisionMaxZ);

                            if
                            (
                                currentX >= collisionMinX && currentX <= collisionMaxX &&
                                currentY >= collisionMinY && currentY <= collisionMaxY &&
                                currentZ >= collisionMinZ && currentZ <= collisionMaxZ
                            )
                            {
                                obstacleDetected = true;
                                break;
                            }
                        }

                        #endregion

                        #region Ground check
                        //GROUND CHECK
                        hasGroundUnderFeet = false;

                        if (!obstacleDetected) //NOTE: ground check is not necessary if player collides with obstacle (performance)
                        {
                            for (int i = 0; i < terrainFilledSize; i++)
                            {
                                geoDataElement = terrainSubArray[i];
                                GetColliderPosition(geoDataElement, out collisionMinX, out collisionMaxX, out collisionMinY, out collisionMaxY, out collisionMinZ, out collisionMaxZ);

                                if
                                (
                                    currentX >= collisionMinX && currentX <= collisionMaxX &&
                                    currentY >= collisionMinY && currentY <= collisionMaxY &&
                                    currentZ >= collisionMinZ - 1 && currentZ <= collisionMaxZ + 2
                                )
                                {
                                    hasGroundUnderFeet = true;
                                    break;
                                }
                            }
                        }

                        #endregion

                        if (obstacleDetected || !hasGroundUnderFeet)
                        {
                            valid = false;
                            break;
                        }
                        else
                        {
                            lastValidMovementPoint.X = currentXDbl;
                            lastValidMovementPoint.Y = currentYDbl;
                            lastValidMovementPoint.Z = currentZDbl - _marginZ;
                        }

                        //Console.WriteLine($"tClamped [{tParamClamped}] pos [{currentX}; {currentY}; {currentZ}] obstacle [{obstacleDetected}]");

                        if (tParam >= 1)
                        {
                            tParamReached = true;
                        }

                        tParam += tStep;
                    }
                }


                details.Valid = valid;
                details.LastValidMovementPoint = lastValidMovementPoint;
                details.LastTParamValue        = tParamClamped;
            }
            catch (Exception exception)
            {
                msg = $"Error occured while geo data movement validation for character's ID [{movementDetails.CharId}]: {exception.Message}";
            }

            data.Data = details;
            data.Msg  = msg;
            return(data);
        }
Beispiel #2
0
        private async void HandleCharactersMovementAsync()
        {
            if (_movementHandlingInProgress)
            {
                _logger.UpdateLog("Character movement handling already started!");
                return;
            }

            _movementHandlingInProgress = true;
            _logger.UpdateLog("Game state handler - character movement handling started!");

            try
            {
                List <CharacterMovementDetails> movementDetailsListTemp = new List <CharacterMovementDetails>();
                CharacterData                  characterData;
                List <GeoDataElement>          geoDataList           = new List <GeoDataElement>();
                CharacterPositionUpdateDetails positionDetailsToSend = null;
                int count = 0;

                int  wmId;
                int  parentObjectId;
                bool isOnWorldMap;

                GeoDataValidationDetails geoDataValidationDetails = null;
                bool            geoMovementValid        = false;
                Point3 <double> lastValidMovementPoint  = new Point3 <double>(0, 0, 0);
                bool            exitPointFound          = false;
                Point3 <double> exitPosition            = null;
                double          lastTParamValue         = 0;
                int             timeArrivalMsIncomplete = 0;
                bool            worldMovingValid        = false;

                do
                {
                    #region Main list item removal
                    //MAIN LIST ITEM REMOVAL
                    lock (_movementDetailsLock)
                    {
                        count = _movementDetailsList.Count;

                        if (count > 0)
                        {
                            movementDetailsListTemp = _movementDetailsList.Clone();
                            foreach (CharacterMovementDetails item in _movementDetailsList)
                            {
                                item.Dispose();
                            }

                            _movementDetailsList.Clear();
                        }
                    }

                    #endregion

                    #region Interval handling
                    //INTERVAL HANDLING
                    if (count == 0)
                    {
                        await Task.Factory.StartNew(() => Thread.Sleep(GameStateHandler._movementHandlingTickMs));

                        continue;
                    }

                    #endregion

                    #region Movement details handling
                    //MOV. DETAILS HANDLING
                    foreach (CharacterMovementDetails movementDetails in movementDetailsListTemp)
                    {
                        if (geoDataList.Count > 0)
                        {
                            geoDataList.Clear();
                        }

                        if (movementDetails.CharId < 0)
                        {
                            _logger.UpdateLog("Movement details handling warning - character's ID less than 0!");
                            continue;
                        }

                        //characterData = await _characterInfo.GetCharacterByNameTaskStart(movementDetails.CharName);
                        characterData = await _characterInfo.GetCharacterByIdTaskStart(movementDetails.CharId);

                        if (characterData == null)
                        {
                            _logger.UpdateLog($"Movement details handling warning - character's ID [{movementDetails.CharId}] not found!");
                            continue;
                        }

                        wmId           = characterData.WmId;
                        parentObjectId = characterData.ParentObjectId;
                        isOnWorldMap   = characterData.IsOnWorldMap;

                        switch (movementDetails.Type)
                        {
                        case CharacterMovementDetails.MovementType.SwitchMovementType:
                        {
                            #region Local enter/exit point veryfication
                            //LOCAL ENTER/EXIT POINT VERYFICATION
                            exitPointFound = false;

                            if (characterData.IsOnWorldMap)
                            {
                                using (BoxedData geoBoxedData = await _geoDataInfo.GetLocalGeoDataOfExitElementsTaskStart(wmId, parentObjectId))
                                {
                                    geoDataList = (List <GeoDataElement>)geoBoxedData.Data;
                                    if (!String.IsNullOrEmpty(geoBoxedData.Msg))
                                    {
                                        _logger.UpdateLog(geoBoxedData.Msg);
                                    }
                                }

                                exitPosition = GeoDataValidator.GetRandomExitPosition(geoDataList);

                                if (exitPosition != null)
                                {
                                    exitPointFound = true;

                                    //PLACING CHARACTER ON ENTRY POINT
                                    characterData.MoveCharacterLocal
                                    (
                                        exitPosition.Copy(),
                                        exitPosition.Copy(),
                                        0
                                    );
                                }
                            }

                            #endregion

                            #region State change section
                            //STATE CHANGE SECTION

                            if (!exitPointFound && characterData.IsOnWorldMap)
                            {
                                _logger.UpdateLog($"Cannot find location exit point for character's ID [{movementDetails.CharId}] wm_id [{wmId}] parent object ID [{parentObjectId}]");
                                SendMessageToPlayer(movementDetails.CharId, "Error: cannot find entry point!");
                            }
                            else
                            {
                                characterData.IsOnWorldMap = !isOnWorldMap;
                                _logger.UpdateLog($"Movement type switched for character's ID [{movementDetails.CharId}] to {(characterData.IsOnWorldMap ? "world" : "local")} state");

                                positionDetailsToSend = new CharacterPositionUpdateDetails()
                                {
                                    MovementType       = (characterData.IsOnWorldMap ? "switchmap" : "switchlocal"),
                                    CharId             = movementDetails.CharId,
                                    OldLocationLocal   = characterData.CurrentLoc.Copy(),
                                    NewLocationLocal   = characterData.CurrentLoc.Copy(),
                                    TimeArrivalMsLocal = 0,
                                    OldLocationWorld   = characterData.CurrentWorldLoc.Copy(),
                                    NewLocationWorld   = characterData.CurrentWorldLoc.Copy()
                                };

                                if (characterData.IsOnWorldMap)
                                {
                                    SendMovementDetailsToCurrentPlayerAsync(positionDetailsToSend, movementDetails.CharId);
                                }

                                SendMovementDetailsToPlayersInLocationAsync(positionDetailsToSend, wmId, false, parentObjectId);
                            }

                            #endregion
                        }
                        break;

                        case CharacterMovementDetails.MovementType.Local:
                        {
                            #region Local movement handling
                            //LOCAL MOVEMENT HANDLING
                            if (isOnWorldMap)
                            {
                                _logger.UpdateLog($"Movement details handling warning - local movement cancelled for character's ID [{movementDetails.CharId}], reason: world map state");
                            }
                            else
                            {
                                #region Geo data validation
                                //GEO DATA VALIDATION
                                using
                                (
                                    BoxedData geoBoxedData = await _geoDataInfo.GetLocalGeoDataTaskStart
                                                             (
                                        wmId,
                                        parentObjectId,
                                        PointConverter.Point3DoubleToInt(movementDetails.OldLocationLocal),
                                        PointConverter.Point3DoubleToInt(movementDetails.NewLocationLocal)
                                                             )
                                )
                                {
                                    geoDataList = (List <GeoDataElement>)geoBoxedData.Data;
                                    if (!String.IsNullOrEmpty(geoBoxedData.Msg))
                                    {
                                        _logger.UpdateLog(geoBoxedData.Msg);
                                    }
                                }

                                using (BoxedData geoValidationBoxedData = await GeoDataValidator.ValidateMovementTaskStart(geoDataList, movementDetails))
                                {
                                    geoDataValidationDetails = (GeoDataValidationDetails)geoValidationBoxedData.Data;
                                    if (!String.IsNullOrEmpty(geoValidationBoxedData.Msg))
                                    {
                                        _logger.UpdateLog(geoValidationBoxedData.Msg);
                                    }

                                    geoMovementValid       = geoDataValidationDetails.Valid;
                                    lastValidMovementPoint = geoDataValidationDetails.LastValidMovementPoint;
                                    lastTParamValue        = geoDataValidationDetails.LastTParamValue;

                                    geoDataValidationDetails.Dispose();
                                    geoDataValidationDetails = null;
                                }
                                #endregion

                                //_logger.UpdateLog($"GEO VALID [{geoMovementValid}]");
                                //geoMovementValid = true; //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                                //CHARACTER MOVING
                                if (geoMovementValid)
                                {
                                    characterData.MoveCharacterLocal
                                    (
                                        movementDetails.OldLocationLocal.Copy(),
                                        movementDetails.NewLocationLocal.Copy(),
                                        movementDetails.TimeArrivalMsLocal
                                    );

                                    positionDetailsToSend = new CharacterPositionUpdateDetails()
                                    {
                                        MovementType       = "movelocal",
                                        CharId             = movementDetails.CharId,
                                        OldLocationLocal   = movementDetails.OldLocationLocal.Copy(),
                                        NewLocationLocal   = movementDetails.NewLocationLocal.Copy(),
                                        TimeArrivalMsLocal = movementDetails.TimeArrivalMsLocal,
                                        OldLocationWorld   = new Point2 <int>(0, 0),
                                        NewLocationWorld   = new Point2 <int>(0, 0)
                                    };

                                    SendMovementDetailsToPlayersInLocationAsync(positionDetailsToSend, wmId, isOnWorldMap, parentObjectId);
                                }
                                else
                                {
                                    timeArrivalMsIncomplete = Convert.ToInt32(Convert.ToDouble(movementDetails.TimeArrivalMsLocal) * lastTParamValue);

                                    characterData.MoveCharacterLocal
                                    (
                                        movementDetails.OldLocationLocal.Copy(),
                                        lastValidMovementPoint.Copy(),
                                        timeArrivalMsIncomplete
                                    );

                                    positionDetailsToSend = new CharacterPositionUpdateDetails()
                                    {
                                        MovementType       = "movelocal",
                                        CharId             = movementDetails.CharId,
                                        OldLocationLocal   = movementDetails.OldLocationLocal.Copy(),
                                        NewLocationLocal   = lastValidMovementPoint.Copy(),
                                        TimeArrivalMsLocal = timeArrivalMsIncomplete,
                                        OldLocationWorld   = new Point2 <int>(0, 0),
                                        NewLocationWorld   = new Point2 <int>(0, 0)
                                    };

                                    SendMovementDetailsToPlayersInLocationAsync(positionDetailsToSend, wmId, isOnWorldMap, parentObjectId);
                                }
                            }

                            #endregion
                        }
                        break;

                        case CharacterMovementDetails.MovementType.World:
                        {
                            #region World map movement handling
                            //WORLD MAP MOVEMENT HANDLING
                            if (!isOnWorldMap)
                            {
                                _logger.UpdateLog($"Movement details handling warning - world map movement cancelled for character's ID [{movementDetails.CharId}], reason: local movement state");
                            }
                            else
                            {
                                Point2 <int> oldLocWorld = movementDetails.OldLocationWorld.Copy();
                                Point2 <int> newLocWorld = movementDetails.NewLocationWorld.Copy();

                                //WORLD MOVEMENT VALIDATION
                                worldMovingValid = true;

                                int distanceX = Math.Abs(oldLocWorld.X - newLocWorld.X);
                                int distanceY = Math.Abs(oldLocWorld.Y - newLocWorld.Y);

                                if
                                (
                                    oldLocWorld.X != characterData.CurrentWorldLoc.X || oldLocWorld.Y != characterData.CurrentWorldLoc.Y ||
                                    distanceX > 2 || distanceY > 2
                                )
                                {
                                    worldMovingValid = false;
                                    //_logger.UpdateLog($"Movement details handling warning - world map movement cancelled for character's ID [{movementDetails.CharId}]");
                                }

                                //CHARACTER MOVING
                                if (worldMovingValid)
                                {
                                    WorldPlaceDataDetails worldPlaceDetails = _geoDataInfo.GetWorldPlaceDataDetails(newLocWorld.X, newLocWorld.Y);
                                    int newWmId = (worldPlaceDetails != null ? worldPlaceDetails.WmId : -1);

                                    characterData.MoveCharacterWorld(newLocWorld);

                                    positionDetailsToSend = new CharacterPositionUpdateDetails()
                                    {
                                        MovementType       = "moveworld",
                                        CharId             = movementDetails.CharId,
                                        OldLocationLocal   = new Point3 <double>(0, 0, 0),
                                        NewLocationLocal   = new Point3 <double>(0, 0, 0),
                                        TimeArrivalMsLocal = 0,
                                        OldLocationWorld   = oldLocWorld,
                                        NewLocationWorld   = newLocWorld
                                    };

                                    SendMovementDetailsToPlayersInLocationAsync(positionDetailsToSend, wmId, isOnWorldMap, parentObjectId);
                                    ChangeWmIdAsync(characterData, newWmId);
                                }
                                else
                                {
                                    SendMessageToPlayer(movementDetails.CharId, "You're moving too far");
                                }
                            }

                            #endregion
                        }
                        break;

                        default:
                            throw new Exception($"unknown movement type [{movementDetails.Type.ToString()}]!");
                        }
                    }

                    #endregion

                    #region Temp. list disposal
                    //TEMP. LIST DISPOSAL
                    foreach (CharacterMovementDetails item in movementDetailsListTemp)
                    {
                        item.Dispose();
                    }

                    movementDetailsListTemp.Clear();

                    #endregion
                }while (_movementHandlingInProgress);
            }
            catch (Exception exception)
            {
                _logger.UpdateLog($"Character movement handling error: {exception.Message}");
            }
            finally
            {
                _movementHandlingInProgress = false;
                _logger.UpdateLog("Game state handler - character movement handling stopped!");
            }
        }