Changeset - r24510:ec6a3064485b
[Not reviewed]
master
0 7 0
Niels Martin Hansen - 4 years ago 2020-06-26 19:30:18
nielsm@indvikleren.dk
Feature: Plant clumps of trees in editor by dragging on the landscape
7 files changed with 153 insertions and 10 deletions:
0 comments (0 inline, 0 general)
src/lang/english.txt
Show inline comments
 
@@ -2542,13 +2542,19 @@ STR_OBJECT_BUILD_SIZE                   
 
STR_OBJECT_CLASS_LTHS                                           :Lighthouses
 
STR_OBJECT_CLASS_TRNS                                           :Transmitters
 

	
 
# Tree planting window (last two for SE only)
 
# Tree planting window (last eight for SE only)
 
STR_PLANT_TREE_CAPTION                                          :{WHITE}Trees
 
STR_PLANT_TREE_TOOLTIP                                          :{BLACK}Select tree type to plant. If the tile already has a tree, this will add more trees of mixed types independent of the selected type
 
STR_TREES_RANDOM_TYPE                                           :{BLACK}Trees of random type
 
STR_TREES_RANDOM_TYPE_TOOLTIP                                   :{BLACK}Place trees of random type. Shift toggles building/showing cost estimate
 
STR_TREES_RANDOM_TREES_BUTTON                                   :{BLACK}Random Trees
 
STR_TREES_RANDOM_TREES_TOOLTIP                                  :{BLACK}Plant trees randomly throughout the landscape
 
STR_TREES_MODE_NORMAL_BUTTON                                    :{BLACK}Normal
 
STR_TREES_MODE_NORMAL_TOOLTIP                                   :{BLACK}Plant single trees by dragging over the landscape.
 
STR_TREES_MODE_FOREST_SM_BUTTON                                 :{BLACK}Grove
 
STR_TREES_MODE_FOREST_SM_TOOLTIP                                :{BLACK}Plant small forests by dragging over the landscape.
 
STR_TREES_MODE_FOREST_LG_BUTTON                                 :{BLACK}Forest
 
STR_TREES_MODE_FOREST_LG_TOOLTIP                                :{BLACK}Plant large forests by dragging over the landscape.
 

	
 
# Land generation window (SE)
 
STR_TERRAFORM_TOOLBAR_LAND_GENERATION_CAPTION                   :{WHITE}Land Generation
src/tilehighlight_func.h
Show inline comments
 
@@ -22,6 +22,7 @@ void SetObjectToPlace(CursorID icon, Pal
 
void ResetObjectToPlace();
 

	
 
void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method);
 
void VpStartDragging(ViewportDragDropSelectionProcess process);
 
void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process);
 
void VpSetPresizeRange(TileIndex from, TileIndex to);
 
void VpSetPlaceSizingLimit(int limit);
src/tree_cmd.cpp
Show inline comments
 
@@ -291,6 +291,51 @@ void PlaceTreesRandomly()
 
}
 

	
 
/**
 
 * Place some trees in a radius around a tile.
 
 * The trees are placed in an quasi-normal distribution around the indicated tile, meaning that while
 
 * the radius does define a square, the distribution inside the square will be roughly circular.
 
 * @note This function the interactive RNG and must only be used in editor and map generation.
 
 * @param tile      Tile to place trees around.
 
 * @param treetype  Type of trees to place. Must be a valid tree type for the climate.
 
 * @param radius    Maximum distance (on each axis) from tile to place trees.
 
 * @param count     Maximum number of trees to place.
 
 * @return Number of trees actually placed.
 
 */
 
uint PlaceTreeGroupAroundTile(TileIndex tile, TreeType treetype, uint radius, uint count)
 
{
 
	assert(treetype < TREE_TOYLAND + TREE_COUNT_TOYLAND);
 
	const bool allow_desert = treetype == TREE_CACTUS;
 
	uint planted = 0;
 

	
 
	for (; count > 0; count--) {
 
		/* Simple quasi-normal distribution with range [-radius; radius) */
 
		auto mkcoord = [&]() -> int32 {
 
			const uint32 rand = InteractiveRandom();
 
			const int32 dist = GB<int32>(rand, 0, 8) + GB<int32>(rand, 8, 8) + GB<int32>(rand, 16, 8) + GB<int32>(rand, 24, 8);
 
			const int32 scu = dist * radius / 512;
 
			return scu - radius;
 
		};
 
		const int32 xofs = mkcoord();
 
		const int32 yofs = mkcoord();
 
		const TileIndex tile_to_plant = TileAddWrap(tile, xofs, yofs);
 
		if (tile_to_plant != INVALID_TILE) {
 
			if (IsTileType(tile_to_plant, MP_TREES) && GetTreeCount(tile_to_plant) < 4) {
 
				AddTreeCount(tile_to_plant, 1);
 
				SetTreeGrowth(tile_to_plant, 0);
 
				MarkTileDirtyByTile(tile_to_plant, 0);
 
				planted++;
 
			} else if (CanPlantTreesOnTile(tile_to_plant, allow_desert)) {
 
				PlantTreesOnTile(tile_to_plant, treetype, 0, 3);
 
				MarkTileDirtyByTile(tile_to_plant, 0);
 
				planted++;
 
			}
 
		}
 
	}
 

	
 
	return planted;
 
}
 

	
 
/**
 
 * Place new trees.
 
 *
 
 * This function takes care of the selected tree placer algorithm and
 
@@ -349,7 +394,7 @@ CommandCost CmdPlantTree(TileIndex tile,
 
		switch (GetTileType(tile)) {
 
			case MP_TREES:
 
				/* no more space for trees? */
 
				if (_game_mode != GM_EDITOR && GetTreeCount(tile) == 4) {
 
				if (GetTreeCount(tile) == 4) {
 
					msg = STR_ERROR_TREE_ALREADY_HERE;
 
					continue;
 
				}
src/tree_gui.cpp
Show inline comments
 
@@ -14,6 +14,7 @@
 
#include "company_func.h"
 
#include "company_base.h"
 
#include "command_func.h"
 
#include "core/random_func.hpp"
 
#include "sound_func.h"
 
#include "strings_func.h"
 
#include "zoom_func.h"
 
@@ -28,6 +29,7 @@
 
#include "safeguards.h"
 

	
 
void PlaceTreesRandomly();
 
uint PlaceTreeGroupAroundTile(TileIndex tile, TreeType treetype, uint radius, uint count);
 

	
 
/** Tree Sprites with their palettes */
 
const PalSpriteID tree_sprites[] = {
 
@@ -79,7 +81,14 @@ class BuildTreesWindow : public Window
 
	/** Visual Y offset of tree root from the bottom of the tree type buttons */
 
	static const int BUTTON_BOTTOM_OFFSET = 7;
 

	
 
	enum PlantingMode {
 
		PM_NORMAL,
 
		PM_FOREST_SM,
 
		PM_FOREST_LG,
 
	};
 

	
 
	int tree_to_plant;  ///< Tree number to plant, \c TREE_INVALID for a random tree.
 
	PlantingMode mode;  ///< Current mode for planting
 

	
 
	/**
 
	 * Update the GUI and enable/disable planting to reflect selected options.
 
@@ -106,15 +115,35 @@ class BuildTreesWindow : public Window
 
			this->LowerWidget(WID_BT_TYPE_BUTTON_FIRST + this->tree_to_plant);
 
		}
 

	
 
		switch (this->mode) {
 
			case PM_NORMAL: this->LowerWidget(WID_BT_MODE_NORMAL); break;
 
			case PM_FOREST_SM: this->LowerWidget(WID_BT_MODE_FOREST_SM); break;
 
			case PM_FOREST_LG: this->LowerWidget(WID_BT_MODE_FOREST_LG); break;
 
			default: NOT_REACHED();
 
		}
 

	
 
		this->SetDirty();
 
	}
 

	
 
	void DoPlantForest(TileIndex tile)
 
	{
 
		TreeType treetype = (TreeType)this->tree_to_plant;
 
		if (this->tree_to_plant == TREE_INVALID) {
 
			treetype = (TreeType)(InteractiveRandomRange(_tree_count_by_landscape[_settings_game.game_creation.landscape]) + _tree_base_by_landscape[_settings_game.game_creation.landscape]);
 
		}
 
		const uint radius = this->mode == PM_FOREST_LG ? 12 : 5;
 
		const uint count = this->mode == PM_FOREST_LG ? 12 : 5;
 
		PlaceTreeGroupAroundTile(tile, treetype, radius, count);
 
	}
 

	
 
public:
 
	BuildTreesWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc), tree_to_plant(-1)
 
	BuildTreesWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc), tree_to_plant(-1), mode(PM_NORMAL)
 
	{
 
		this->InitNested(window_number);
 
		ResetObjectToPlace();
 

	
 
		this->LowerWidget(WID_BT_MODE_NORMAL);
 

	
 
		/* Show scenario editor tools in editor */
 
		auto *se_tools = this->GetWidget<NWidgetStacked>(WID_BT_SE_PANE);
 
		if (_game_mode != GM_EDITOR) {
 
@@ -156,6 +185,23 @@ public:
 
				MarkWholeScreenDirty();
 
				break;
 

	
 
			case WID_BT_MODE_NORMAL:
 
				this->mode = PM_NORMAL;
 
				this->UpdateMode();
 
				break;
 

	
 
			case WID_BT_MODE_FOREST_SM:
 
				assert(_game_mode == GM_EDITOR);
 
				this->mode = PM_FOREST_SM;
 
				this->UpdateMode();
 
				break;
 

	
 
			case WID_BT_MODE_FOREST_LG:
 
				assert(_game_mode == GM_EDITOR);
 
				this->mode = PM_FOREST_LG;
 
				this->UpdateMode();
 
				break;
 

	
 
			default:
 
				if (widget >= WID_BT_TYPE_BUTTON_FIRST) {
 
					const int index = widget - WID_BT_TYPE_BUTTON_FIRST;
 
@@ -168,17 +214,31 @@ public:
 

	
 
	void OnPlaceObject(Point pt, TileIndex tile) override
 
	{
 
		VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_PLANT_TREES);
 
		if (_game_mode != GM_EDITOR && this->mode == PM_NORMAL) {
 
			VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_PLANT_TREES);
 
		} else {
 
			VpStartDragging(DDSP_PLANT_TREES);
 
		}
 
	}
 

	
 
	void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) override
 
	{
 
		VpSelectTilesWithMethod(pt.x, pt.y, select_method);
 
		if (_game_mode != GM_EDITOR && this->mode == PM_NORMAL) {
 
			VpSelectTilesWithMethod(pt.x, pt.y, select_method);
 
		} else {
 
			TileIndex tile = TileVirtXY(pt.x, pt.y);
 

	
 
			if (this->mode == PM_NORMAL) {
 
				DoCommandP(tile, this->tree_to_plant, tile, CMD_PLANT_TREE);
 
			} else {
 
				this->DoPlantForest(tile);
 
			}
 
		}
 
	}
 

	
 
	void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) override
 
	{
 
		if (pt.x != -1 && select_proc == DDSP_PLANT_TREES) {
 
		if (_game_mode != GM_EDITOR && this->mode == PM_NORMAL && pt.x != -1 && select_proc == DDSP_PLANT_TREES) {
 
			DoCommandP(end_tile, this->tree_to_plant, start_tile, CMD_PLANT_TREE | CMD_MSG(STR_ERROR_CAN_T_PLANT_TREE_HERE));
 
		}
 
	}
 
@@ -241,6 +301,12 @@ static const NWidgetPart _nested_build_t
 
			NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BT_SE_PANE),
 
				NWidget(NWID_VERTICAL),
 
					NWidget(NWID_SPACER), SetMinimalSize(0, 1),
 
					NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 
						NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BT_MODE_NORMAL), SetFill(1, 0), SetDataTip(STR_TREES_MODE_NORMAL_BUTTON, STR_TREES_MODE_NORMAL_TOOLTIP),
 
						NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BT_MODE_FOREST_SM), SetFill(1, 0), SetDataTip(STR_TREES_MODE_FOREST_SM_BUTTON, STR_TREES_MODE_FOREST_SM_TOOLTIP),
 
						NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BT_MODE_FOREST_LG), SetFill(1, 0), SetDataTip(STR_TREES_MODE_FOREST_LG_BUTTON, STR_TREES_MODE_FOREST_LG_TOOLTIP),
 
					EndContainer(),
 
					NWidget(NWID_SPACER), SetMinimalSize(0, 1),
 
					NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BT_MANY_RANDOM), SetDataTip(STR_TREES_RANDOM_TREES_BUTTON, STR_TREES_RANDOM_TREES_TOOLTIP),
 
				EndContainer(),
 
			EndContainer(),
src/viewport.cpp
Show inline comments
 
@@ -2674,6 +2674,18 @@ void VpStartPlaceSizing(TileIndex tile, 
 
	_special_mouse_mode = WSM_SIZING;
 
}
 

	
 
/** Drag over the map while holding the left mouse down. */
 
void VpStartDragging(ViewportDragDropSelectionProcess process)
 
{
 
	_thd.select_method = VPM_X_AND_Y;
 
	_thd.select_proc = process;
 
	_thd.selstart.x = 0;
 
	_thd.selstart.y = 0;
 
	_thd.next_drawstyle = HT_RECT;
 

	
 
	_special_mouse_mode = WSM_DRAGGING;
 
}
 

	
 
void VpSetPlaceSizingLimit(int limit)
 
{
 
	_thd.sizelimit = limit;
 
@@ -3283,7 +3295,7 @@ calc_heightdiff_single_direction:;
 
 */
 
EventState VpHandlePlaceSizingDrag()
 
{
 
	if (_special_mouse_mode != WSM_SIZING) return ES_NOT_HANDLED;
 
	if (_special_mouse_mode != WSM_SIZING && _special_mouse_mode != WSM_DRAGGING) return ES_NOT_HANDLED;
 

	
 
	/* stop drag mode if the window has been closed */
 
	Window *w = _thd.GetCallbackWnd();
 
@@ -3294,13 +3306,22 @@ EventState VpHandlePlaceSizingDrag()
 

	
 
	/* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
 
	if (_left_button_down) {
 
		if (_special_mouse_mode == WSM_DRAGGING) {
 
			/* Only register a drag event when the mouse moved. */
 
			if (_thd.new_pos.x == _thd.selstart.x && _thd.new_pos.y == _thd.selstart.y) return ES_HANDLED;
 
			_thd.selstart.x = _thd.new_pos.x;
 
			_thd.selstart.y = _thd.new_pos.y;
 
		}
 

	
 
		w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor());
 
		return ES_HANDLED;
 
	}
 

	
 
	/* mouse button released..
 
	 * keep the selected tool, but reset it to the original mode. */
 
	/* Mouse button released. */
 
	_special_mouse_mode = WSM_NONE;
 
	if (_special_mouse_mode == WSM_DRAGGING) return ES_HANDLED;
 

	
 
	/* Keep the selected tool, but reset it to the original mode. */
 
	HighLightStyle others = _thd.place_mode & ~(HT_DRAG_MASK | HT_DIR_MASK);
 
	if ((_thd.next_drawstyle & HT_DRAG_MASK) == HT_RECT) {
 
		_thd.place_mode = HT_RECT | others;
src/widgets/tree_widget.h
Show inline comments
 
@@ -14,6 +14,9 @@
 
enum BuildTreesWidgets {
 
	WID_BT_TYPE_RANDOM,     ///< Button to build random type of tree.
 
	WID_BT_SE_PANE,         ///< Selection pane to show/hide scenario editor tools.
 
	WID_BT_MODE_NORMAL,     ///< Select normal/rectangle planting mode.
 
	WID_BT_MODE_FOREST_SM,  ///< Select small forest planting mode.
 
	WID_BT_MODE_FOREST_LG,  ///< Select large forest planting mode.
 
	WID_BT_MANY_RANDOM,     ///< Button to build many random trees.
 
	WID_BT_TYPE_BUTTON_FIRST, ///< First tree type selection button. (This must be last in the enum.)
 
};
src/window_gui.h
Show inline comments
 
@@ -903,9 +903,10 @@ extern bool _mouse_hovering;
 
/** Mouse modes. */
 
enum SpecialMouseMode {
 
	WSM_NONE,     ///< No special mouse mode.
 
	WSM_DRAGDROP, ///< Dragging an object.
 
	WSM_DRAGDROP, ///< Drag&drop an object.
 
	WSM_SIZING,   ///< Sizing mode.
 
	WSM_PRESIZE,  ///< Presizing mode (docks, tunnels).
 
	WSM_DRAGGING, ///< Dragging mode (trees).
 
};
 
extern SpecialMouseMode _special_mouse_mode;
 

	
0 comments (0 inline, 0 general)