/** * Show a dialog so as to get a text from user */ bool GD_EXTENSION_API ShowTextInput( RuntimeScene & scene, gd::Variable & variable, const gd::String & message, const gd::String & title ) { sf::Clock timeSpent; gd::String result; //Display the box #if defined(WINDOWS) CInputBox ibox(NULL); if (ibox.DoModal(title.ToWide().c_str(), message.ToWide().c_str())) result = gd::String::FromWide(ibox.Text); #endif #if defined(LINUX) || defined(MACOS) std::string strResult; nw::TextInput dialog(title.ToLocale(), message.ToLocale(), strResult); dialog.wait_until_closed(); result = gd::String::FromLocale(strResult); //Convert from locale #endif scene.GetTimeManager().NotifyPauseWasMade(timeSpent.getElapsedTime().asMicroseconds());//Don't take the time spent in this function in account. //Update the variable variable.SetString(result); return true; }
void GD_API PlaySound( RuntimeScene & scene, const gd::String & file, bool repeat, float volume, float pitch ) { sf::Clock latency; scene.game->GetSoundManager().PlaySound(file, repeat, volume, pitch); scene.GetTimeManager().NotifyPauseWasMade(latency.getElapsedTime().asMicroseconds()); }
/** * Display a simple message box. */ void GD_EXTENSION_API ShowMessageBox( RuntimeScene & scene, const gd::String & message, const gd::String & title ) { sf::Clock timeSpent; //Display the box #if defined(WINDOWS) MessageBoxW(NULL, message.ToWide().c_str(), title.ToWide().c_str(), MB_ICONINFORMATION); #endif #if defined(LINUX) || defined(MACOS) nw::MsgBox msgBox(title.ToLocale(), message.ToLocale()); msgBox.wait_until_closed(); #endif scene.GetTimeManager().NotifyPauseWasMade(timeSpent.getElapsedTime().asMicroseconds());//Don't take the time spent in this function in account. }
void GD_API MoveObjects( RuntimeScene & scene ) { RuntimeObjList allObjects = scene.objectsInstances.GetAllObjects(); double elapsedTime = static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0; for (std::size_t id = 0;id < allObjects.size();++id) { allObjects[id]->SetX(allObjects[id]->GetX() + allObjects[id]->TotalForceX() * elapsedTime); allObjects[id]->SetY(allObjects[id]->GetY() + allObjects[id]->TotalForceY() * elapsedTime); allObjects[id]->UpdateForce(elapsedTime); } return; }
void GD_API CenterCameraOnObject(RuntimeScene & scene, RuntimeObject * object, bool anticipateObjectMove, const gd::String & layer, std::size_t camera) { if ( object == NULL ) return; float xOffset = 0; float yOffset = 0; double elapsedTime = static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0; if (anticipateObjectMove) { xOffset = object->TotalForceX() * elapsedTime; yOffset = object->TotalForceY() * elapsedTime; } scene.GetRuntimeLayer(layer).GetCamera(camera).SetViewCenter(sf::Vector2f(object->GetDrawableX() + object->GetCenterX() + xOffset, object->GetDrawableY() + object->GetCenterY() + yOffset)); return; }
void RuntimeObject::RotateTowardAngle(float angleInDegrees, float speed, RuntimeScene & scene) { if (speed == 0) { SetAngle(angleInDegrees); return; } float timeDelta = static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0; float angularDiff = GDpriv::MathematicalTools::angleDifference(GetAngle(), angleInDegrees); bool diffWasPositive = angularDiff >= 0; float newAngle = GetAngle()+(diffWasPositive ? -1.0 : 1.0)*speed*timeDelta; if((GDpriv::MathematicalTools::angleDifference(newAngle, angleInDegrees) > 0) ^ diffWasPositive) newAngle = angleInDegrees; SetAngle(newAngle); if (GetAngle() != newAngle) //Objects like sprite in 8 directions does not handle small increments... SetAngle(angleInDegrees); //...so force them to be in the path angle anyway. }
void PathfindingBehavior::DoStepPreEvents(RuntimeScene & scene) { if ( parentScene != &scene ) //Parent scene has changed { parentScene = &scene; sceneManager = parentScene ? &ScenePathfindingObstaclesManager::managers[&scene] : NULL; } if ( !sceneManager ) return; if (path.empty() || reachedEnd) return; //Update the speed of the object float timeDelta = static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0; speed += acceleration*timeDelta; if ( speed > maxSpeed ) speed = maxSpeed; angularSpeed = angularMaxSpeed; //No acceleration for angular speed for now //Update the time on the segment and change segment if needed timeOnSegment += speed*timeDelta; if (timeOnSegment >= totalSegmentTime && currentSegment < path.size()) EnterSegment(currentSegment + 1); //Position object on the segment and update its angle sf::Vector2f newPos; float pathAngle = object->GetAngle(); if ( currentSegment < path.size()-1 ) { newPos = path[currentSegment] + (path[currentSegment + 1] - path[currentSegment]) * (timeOnSegment / totalSegmentTime); pathAngle = atan2(path[currentSegment+1].y - path[currentSegment].y, path[currentSegment+1].x - path[currentSegment].x)*180/3.14159+angleOffset; } else newPos = path.back(); object->SetX(newPos.x); object->SetY(newPos.y); //Also update angle if needed if ( rotateObject ) object->RotateTowardAngle(pathAngle, angularSpeed, scene); }
/** * Called at each frame before events : * Position the object on the path */ void PathBehavior::DoStepPreEvents(RuntimeScene & scene) { if(!isPathLoaded) { LoadPath(scene); Reset(); } // add to the current time along the path timeOnSegment += static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0 * speed; // if I reached the end of this segment, move to a new segment if (timeOnSegment >= totalSegmentTime && currentSegment < path.size()) EnterSegment(currentSegment + 1); //Position object on the segment sf::Vector2f newPos; if ( !path.empty() && currentSegment < path.size()-1 ) newPos = offset + path[currentSegment] + (path[currentSegment + 1] - path[currentSegment]) * (timeOnSegment / totalSegmentTime); else { if ( stopAtEnd && !path.empty()) newPos = path.back() + offset; else if (reverseAtEnd) { std::reverse(path.begin(), path.end()); EnterSegment(0); if (!path.empty()) newPos = path.front() + offset; } else { EnterSegment(0); if (!path.empty()) newPos = path.front() + offset; } } object->SetX(newPos.x); object->SetY(newPos.y); return; }
/** * Called at each frame before events : * Simulate the world if necessary and update body positions. */ void PhysicsBehavior::DoStepPreEvents(RuntimeScene & scene) { if ( !body ) CreateBody(scene); if ( !runtimeScenesPhysicsDatas->stepped ) //Simulate the world, once at each frame { runtimeScenesPhysicsDatas->StepWorld(static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0, 6, 10); runtimeScenesPhysicsDatas->stepped = true; } //Update object position according to Box2D body b2Vec2 position = body->GetPosition(); object->SetX(position.x*runtimeScenesPhysicsDatas->GetScaleX()-object->GetWidth()/2+object->GetX()-object->GetDrawableX()); object->SetY(-position.y*runtimeScenesPhysicsDatas->GetScaleY()-object->GetHeight()/2+object->GetY()-object->GetDrawableY()); //Y axis is inverted object->SetAngle(-body->GetAngle()*180.0f/b2_pi); //Angles are inverted objectOldX = object->GetX(); objectOldY = object->GetY(); objectOldAngle = object->GetAngle(); };
/** * Display an "open file" dialog */ void GD_EXTENSION_API ShowOpenFile( RuntimeScene & scene, gd::Variable & variable, const gd::String & title, gd::String filters ) { sf::Clock timeSpent; gd::String result; //Display the dialog #if defined(WINDOWS) //Process filters to match windows dialogs filters style. filters.Raw() = filters.Raw()+'\0'; std::replace(filters.Raw().begin(), filters.Raw().end(), '|', '\0'); OPENFILENAMEW toGetFileName; //Struct for the dialog wchar_t filePath[MAX_PATH]; _wgetcwd(filePath, MAX_PATH); ZeroMemory(&toGetFileName, sizeof(OPENFILENAMEW)); toGetFileName.lStructSize = sizeof(OPENFILENAMEW); toGetFileName.hwndOwner = NULL; toGetFileName.lpstrFile = filePath; toGetFileName.nMaxFile = MAX_PATH; toGetFileName.lpstrFilter = filters == "\0" ? NULL : filters.ToWide().c_str(); toGetFileName.nFilterIndex = 1; toGetFileName.Flags = OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR;; if(GetOpenFileNameW(&toGetFileName) == TRUE) result = gd::String::FromWide(filePath); #endif #if defined(LINUX) || defined(MACOS) std::string strResult; nw::OpenFile * dialog = new nw::OpenFile(title.ToLocale(), true, strResult); dialog->wait_until_closed(); result = gd::String::FromLocale(strResult); #endif scene.GetTimeManager().NotifyPauseWasMade(timeSpent.getElapsedTime().asMicroseconds());//Don't take the time spent in this function in account. //Update the variable variable.SetString(result); }
void GD_API CenterCameraOnObjectWithLimits(RuntimeScene & scene, RuntimeObject * object, float left, float top, float right, float bottom, bool anticipateObjectMove, const gd::String & layer, std::size_t camera) { if ( object == NULL ) return; float xOffset = 0; float yOffset = 0; double elapsedTime = static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0; if (anticipateObjectMove) { xOffset = object->TotalForceX() * elapsedTime; yOffset = object->TotalForceY() * elapsedTime; } RuntimeCamera & cam = scene.GetRuntimeLayer(layer).GetCamera(camera); double newX = std::min(std::max(object->GetDrawableX() + object->GetCenterX() + xOffset, left+cam.GetWidth()/2), right-cam.GetWidth()/2); double newY = std::min(std::max(object->GetDrawableY() + object->GetCenterY() + yOffset, top+cam.GetHeight()/2), bottom-cam.GetHeight()/2); cam.SetViewCenter(sf::Vector2f(newX, newY)); return; }
/** * Show a message box with Yes/No buttons */ void GD_EXTENSION_API ShowYesNoMsgBox( RuntimeScene & scene, gd::Variable & variable, const gd::String & message, const gd::String & title ) { sf::Clock timeSpent; gd::String result; //Display the box #if defined(WINDOWS) if( MessageBoxW(NULL, message.ToWide().c_str(), title.ToWide().c_str(), MB_ICONQUESTION | MB_YESNO) == IDYES) result = "yes"; else result = "no"; #endif #if defined(LINUX) || defined(MACOS) nw::YesNoMsgBox dialog(title.ToLocale(), message.ToLocale(), result.Raw()); dialog.wait_until_closed(); #endif scene.GetTimeManager().NotifyPauseWasMade(timeSpent.getElapsedTime().asMicroseconds());//Don't take the time spent in this function in account. //Update the variable variable.SetString(result); //Can only be "yes" or "no", no need to encode in UTF8 }
void PlatformerObjectBehavior::DoStepPreEvents(RuntimeScene& scene) { if (parentScene != &scene) // Parent scene has changed { parentScene = &scene; sceneManager = parentScene ? &ScenePlatformObjectsManager::managers[&scene] : NULL; floorPlatform = NULL; } if (!sceneManager) return; double timeDelta = static_cast<double>(object->GetElapsedTime(scene)) / 1000000.0; // 0.1) Get the player input: double requestedDeltaX = 0; double requestedDeltaY = 0; // Change the speed according to the player's input. leftKey |= !ignoreDefaultControls && scene.GetInputManager().IsKeyPressed("Left"); rightKey |= !ignoreDefaultControls && scene.GetInputManager().IsKeyPressed("Right"); if (leftKey) currentSpeed -= acceleration * timeDelta; if (rightKey) currentSpeed += acceleration * timeDelta; // Take deceleration into account only if no key is pressed. if (leftKey == rightKey) { bool wasPositive = currentSpeed > 0; currentSpeed -= deceleration * timeDelta * (wasPositive ? 1.0 : -1.0); // Set the speed to 0 if the speed was top low. if (wasPositive && currentSpeed < 0) currentSpeed = 0; if (!wasPositive && currentSpeed > 0) currentSpeed = 0; } if (currentSpeed > maxSpeed) currentSpeed = maxSpeed; if (currentSpeed < -maxSpeed) currentSpeed = -maxSpeed; requestedDeltaX += currentSpeed * timeDelta; // Compute the list of the objects that will be used std::set<PlatformBehavior*> potentialObjects = GetPotentialCollidingObjects(std::max(requestedDeltaX, maxFallingSpeed)); std::set<PlatformBehavior*> overlappedJumpThru = GetJumpthruCollidingWith(potentialObjects); // Check that the floor object still exists and is near the object. if (isOnFloor && potentialObjects.find(floorPlatform) == potentialObjects.end()) { isOnFloor = false; floorPlatform = NULL; } // Check that the grabbed platform object still exists and is near the object. if (isGrabbingPlatform && potentialObjects.find(grabbedPlatform) == potentialObjects.end()) { ReleaseGrabbedPlatform(); } // 0.2) Track changes in object size // Stick the object to the floor if its height has changed. if (trackSize && isOnFloor && oldHeight != object->GetHeight() && !scene.GetTimeManager().IsFirstLoop()) { object->SetY(floorLastY - object->GetHeight() + (object->GetY() - object->GetDrawableY()) - 1); } oldHeight = object->GetHeight(); // 1) X axis: // Shift the object according to the floor movement. if (isOnFloor) { requestedDeltaX += floorPlatform->GetObject()->GetX() - floorLastX; requestedDeltaY += floorPlatform->GetObject()->GetY() - floorLastY; } // Shift the object according to the grabbed platform movement. if (isGrabbingPlatform) { // This erases any other movement requestedDeltaX = grabbedPlatform->GetObject()->GetX() - grabbedPlatformLastX; requestedDeltaY = grabbedPlatform->GetObject()->GetY() - grabbedPlatformLastY; } // Ensure the object is not stuck if (SeparateFromPlatforms(potentialObjects, true)) { canJump = true; // After being unstuck, the object must be able to jump again. } // Move the object on x axis. double oldX = object->GetX(); if (requestedDeltaX != 0) { object->SetX(object->GetX() + requestedDeltaX); bool tryRounding = true; // Colliding: Try to push out from the solid. // Note that jump thru are never obstacle on X axis. while (IsCollidingWith( potentialObjects, floorPlatform, /*excludeJumpthrus=*/true)) { if ((requestedDeltaX > 0 && object->GetX() <= oldX) || (requestedDeltaX < 0 && object->GetX() >= oldX)) { object->SetX(oldX); // Unable to move the object without being stuck in // an obstacle. break; } // If on floor: try get up a bit to bypass not perfectly aligned floors. if (isOnFloor) { object->SetY(object->GetY() - 1); if (!IsCollidingWith( potentialObjects, floorPlatform, /*excludeJumpthrus=*/true)) break; object->SetY(object->GetY() + 1); } if (tryRounding) { // First try rounding the position as this might be sufficient to get // the object out of the wall. object->SetX(GDRound(object->GetX())); tryRounding = false; } else { object->SetX(GDRound(object->GetX()) + (requestedDeltaX > 0 ? -1 : 1)); } currentSpeed = 0; // Collided with a wall } } // 2) Y axis: // Go on a ladder ladderKey |= !ignoreDefaultControls && scene.GetInputManager().IsKeyPressed("Up"); if (ladderKey && IsOverlappingLadder(potentialObjects)) { canJump = true; isOnFloor = false; floorPlatform = NULL; currentJumpSpeed = 0; currentFallSpeed = 0; isOnLadder = true; } if (isOnLadder) { upKey |= !ignoreDefaultControls && scene.GetInputManager().IsKeyPressed("Up"); downKey |= !ignoreDefaultControls && scene.GetInputManager().IsKeyPressed("Down"); if (upKey) requestedDeltaY -= 150 * timeDelta; if (downKey) requestedDeltaY += 150 * timeDelta; // Coming to an extremity of a ladder if (!IsOverlappingLadder(potentialObjects)) { isOnLadder = false; } } // Fall if (!isOnFloor && !isOnLadder && !isGrabbingPlatform) { currentFallSpeed += gravity * timeDelta; if (currentFallSpeed > maxFallingSpeed) currentFallSpeed = maxFallingSpeed; requestedDeltaY += currentFallSpeed * timeDelta; requestedDeltaY = std::min(requestedDeltaY, maxFallingSpeed * timeDelta); } // Grabbing a platform if (canGrabPlatforms && requestedDeltaX != 0 && !isOnLadder && !isOnFloor) { bool tryGrabbingPlatform = false; object->SetX(object->GetX() + (requestedDeltaX > 0 ? xGrabTolerance : -xGrabTolerance)); auto collidingObjects = GetPlatformsCollidingWith(potentialObjects, overlappedJumpThru); if (!collidingObjects.empty() && CanGrab(*collidingObjects.begin(), requestedDeltaY)) { tryGrabbingPlatform = true; } object->SetX(object->GetX() + (requestedDeltaX > 0 ? -xGrabTolerance : xGrabTolerance)); // Check if we can grab the collided platform if (tryGrabbingPlatform) { double oldY = object->GetY(); PlatformBehavior* collidingPlatform = *collidingObjects.begin(); object->SetY(collidingPlatform->GetObject()->GetY() + collidingPlatform->GetYGrabOffset() - yGrabOffset); if (!IsCollidingWith(potentialObjects, NULL, /*excludeJumpthrus=*/true)) { isGrabbingPlatform = true; grabbedPlatform = collidingPlatform; requestedDeltaY = 0; } else { object->SetY(oldY); } } } releaseKey |= !ignoreDefaultControls && scene.GetInputManager().IsKeyPressed("Down"); if (isGrabbingPlatform && !releaseKey) { canJump = true; currentJumpSpeed = 0; currentFallSpeed = 0; grabbedPlatformLastX = grabbedPlatform->GetObject()->GetX(); grabbedPlatformLastY = grabbedPlatform->GetObject()->GetY(); } if (releaseKey) ReleaseGrabbedPlatform(); // Jumping jumpKey |= !ignoreDefaultControls && (scene.GetInputManager().IsKeyPressed("LShift") || scene.GetInputManager().IsKeyPressed("RShift") || scene.GetInputManager().IsKeyPressed("Space")); if (canJump && jumpKey) { jumping = true; canJump = false; // isOnFloor = false; If floor is a very steep slope, the object could go // into it. isOnLadder = false; currentJumpSpeed = jumpSpeed; currentFallSpeed = 0; isGrabbingPlatform = false; // object->SetY(object->GetY()-1); } if (jumping) { requestedDeltaY -= currentJumpSpeed * timeDelta; currentJumpSpeed -= gravity * timeDelta; if (currentJumpSpeed < 0) { currentJumpSpeed = 0; jumping = false; } } // Follow the floor if (isOnFloor) { if (object->IsCollidingWith(floorPlatform->GetObject(), ignoreTouchingEdges)) { // Floor is getting up, as the object is colliding with it. double oldY = object->GetY(); int step = 0; bool stillInFloor = false; do { if (step >= floor(std::abs( requestedDeltaX * slopeClimbingFactor))) // Slope is too step ( > max angle ) { object->SetY(object->GetY() - (std::abs(requestedDeltaX * slopeClimbingFactor) - (double)step)); // Try to add the decimal part. if (object->IsCollidingWith(floorPlatform->GetObject(), ignoreTouchingEdges)) stillInFloor = true; // Too steep. break; } // Try to get out of the floor. object->SetY(object->GetY() - 1); step++; } while (object->IsCollidingWith(floorPlatform->GetObject(), ignoreTouchingEdges)); if (stillInFloor) { object->SetY(oldY); // Unable to follow the floor ( too steep ): Go // back to the original position. object->SetX(oldX); // And also revert the shift on X axis. } } else { // Floor is flat or get down. double oldY = object->GetY(); double tentativeStartY = object->GetY() + 1; object->SetY(roundCoordinates ? GDRound(tentativeStartY) : tentativeStartY); int step = 0; bool noMoreOnFloor = false; while (!IsCollidingWith(potentialObjects)) { if (step > std::abs(requestedDeltaX * slopeClimbingFactor)) // Slope is too step ( > 50% ) { noMoreOnFloor = true; break; } // Object was on floor, but no more: Maybe a slope, try to follow it. object->SetY(object->GetY() + 1); step++; } if (noMoreOnFloor) object->SetY(oldY); // Unable to follow the floor: Go back to the // original position. Fall will be triggered next // tick. else object->SetY(object->GetY() - 1); // Floor touched: Go back 1 pixel over. } } // Move the object on Y axis if (requestedDeltaY != 0) { double oldY = object->GetY(); object->SetY(object->GetY() + requestedDeltaY); // Stop when colliding with an obstacle. while ((requestedDeltaY < 0 && IsCollidingWith(potentialObjects, NULL, /*excludeJumpThrus=*/true)) // Jumpthru = obstacle // <=> Never when going // up || (requestedDeltaY > 0 && IsCollidingWith(potentialObjects, overlappedJumpThru))) // Jumpthru = obstacle <=> // Only if not already // overlapped when goign // down { jumping = false; currentJumpSpeed = 0; if ((requestedDeltaY > 0 && object->GetY() <= oldY) || (requestedDeltaY < 0 && object->GetY() >= oldY)) { object->SetY(oldY); // Unable to move the object without being stuck in // an obstacle. break; } object->SetY(floor(object->GetY()) + (requestedDeltaY > 0 ? -1 : 1)); } } // 3) Update the current floor data for the next tick: overlappedJumpThru = GetJumpthruCollidingWith(potentialObjects); if (!isOnLadder) { // Check if the object is on a floor: // In priority, check if the last floor platform is still the floor. double oldY = object->GetY(); object->SetY(object->GetY() + 1); if (isOnFloor && object->IsCollidingWith(floorPlatform->GetObject(), ignoreTouchingEdges)) { // Still on the same floor floorLastX = floorPlatform->GetObject()->GetX(); floorLastY = floorPlatform->GetObject()->GetY(); } else { // Check if landing on a new floor: (Exclude already overlapped jump truh) std::set<PlatformBehavior*> collidingObjects = GetPlatformsCollidingWith(potentialObjects, overlappedJumpThru); if (!collidingObjects.empty()) // Just landed on floor { isOnFloor = true; canJump = true; jumping = false; currentJumpSpeed = 0; currentFallSpeed = 0; floorPlatform = *collidingObjects.begin(); floorLastX = floorPlatform->GetObject()->GetX(); floorLastY = floorPlatform->GetObject()->GetY(); ReleaseGrabbedPlatform(); // Ensure nothing is grabbed. } else // In the air { canJump = false; isOnFloor = false; floorPlatform = NULL; } } object->SetY(oldY); } // 4) Do not forget to reset pressed keys leftKey = false; rightKey = false; ladderKey = false; upKey = false; downKey = false; jumpKey = false; releaseKey = false; // 5) Track the movement hasReallyMoved = std::abs(object->GetX() - oldX) >= 1; }
bool GD_API SceneJustBegins(RuntimeScene & scene ) { return scene.GetTimeManager().IsFirstLoop(); }
void RuntimeObject::Rotate(float speed, RuntimeScene & scene) { float timeDelta = static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0; SetAngle(GetAngle()+speed*timeDelta); }