Ejemplo n.º 1
0
static HandleInputResult HandleInput(
	int c, int m, int *xc, int *yc, int *xcOld, int *ycOld, Mission *scrap)
{
	HandleInputResult result = { false, false, false, false };
	Mission *mission = CampaignGetCurrentMission(&gCampaign);
	UIObject *o = NULL;
	brush.Pos = GetMouseTile(&gEventHandlers);

	// Find whether the mouse has hovered over a tooltip
	bool hadTooltip = sTooltipObj != NULL;
	if (!UITryGetObject(sObjs, gEventHandlers.mouse.currentPos, &sTooltipObj) ||
		!sTooltipObj->Tooltip)
	{
		sTooltipObj = NULL;
	}
	// Need to redraw if we either had a tooltip (draw to remove) or there's a
	// tooltip to draw
	if (hadTooltip || sTooltipObj)
	{
		result.Redraw = true;
	}

	// Make sure a redraw is done immediately if the resolution changes
	// Otherwise the resolution change is ignored and we try to redraw
	// later, when the draw buffer has not yet been recreated
	if (gEventHandlers.HasResolutionChanged)
	{
		result.Redraw = true;
	}

	// Also need to redraw if the brush is active to update the highlight
	if (brush.IsActive)
	{
		result.Redraw = true;
	}

	if (m &&
		(m == SDL_BUTTON_LEFT || m == SDL_BUTTON_RIGHT ||
		m == SDL_BUTTON_WHEELUP || m == SDL_BUTTON_WHEELDOWN))
	{
		result.Redraw = true;
		if (UITryGetObject(sObjs, gEventHandlers.mouse.currentPos, &o))
		{
			if (!o->DoNotHighlight)
			{
				if (sLastHighlightedObj)
				{
					UIObjectUnhighlight(sLastHighlightedObj);
				}
				sLastHighlightedObj = o;
				UIObjectHighlight(o);
			}
			CArrayTerminate(&sDrawObjs);
			*xcOld = *xc;
			*ycOld = *yc;
			// Only change selection on left/right click
			if (m == SDL_BUTTON_LEFT || m == SDL_BUTTON_RIGHT)
			{
				if (!(o->Flags & UI_LEAVE_YC))
				{
					*yc = o->Id;
					AdjustYC(yc);
				}
				if (!(o->Flags & UI_LEAVE_XC))
				{
					*xc = o->Id2;
					AdjustXC(*yc, xc);
				}
			}
			if (!(o->Flags & UI_SELECT_ONLY) &&
				(!(o->Flags & UI_SELECT_ONLY_FIRST) || (*xc == *xcOld && *yc == *ycOld)))
			{
				if (m == SDL_BUTTON_LEFT || m == SDL_BUTTON_WHEELUP)
				{
					c = SDLK_PAGEUP;
				}
				else if (m == SDL_BUTTON_RIGHT || m == SDL_BUTTON_WHEELDOWN)
				{
					c = SDLK_PAGEDOWN;
				}
			}
		}
		else
		{
			if (!(brush.IsActive && mission))
			{
				UIObjectUnhighlight(sObjs);
				CArrayTerminate(&sDrawObjs);
				sLastHighlightedObj = NULL;
			}
		}
	}
	if (!o &&
		(MouseIsDown(&gEventHandlers.mouse, SDL_BUTTON_LEFT) ||
		MouseIsDown(&gEventHandlers.mouse, SDL_BUTTON_RIGHT)))
	{
		result.Redraw = true;
		if (brush.IsActive && mission->Type == MAPTYPE_STATIC)
		{
			// Draw a tile
			if (IsBrushPosValid(brush.Pos, mission))
			{
				int isMain =
					MouseIsDown(&gEventHandlers.mouse, SDL_BUTTON_LEFT);
				EditorResult r =
					EditorBrushStartPainting(&brush, mission, isMain);
				if (r == EDITOR_RESULT_CHANGED ||
					r == EDITOR_RESULT_CHANGED_AND_RELOAD)
				{
					fileChanged = 1;
					Autosave();
					result.RemakeBg = true;
					sHasUnbakedChanges = true;
				}
				if (r == EDITOR_RESULT_CHANGED_AND_RELOAD)
				{
					Setup(0);
				}
			}
		}
	}
	else
	{
		if (mission)
		{
			// Clamp brush position
			brush.Pos = Vec2iClamp(
				brush.Pos,
				Vec2iZero(), Vec2iMinus(mission->Size, Vec2iUnit()));
			EditorResult r = EditorBrushStopPainting(&brush, mission);
			if (r == EDITOR_RESULT_CHANGED ||
				r == EDITOR_RESULT_CHANGED_AND_RELOAD)
			{
				fileChanged = 1;
				Autosave();
				result.Redraw = true;
				result.RemakeBg = true;
				sHasUnbakedChanges = true;
			}
			if (r == EDITOR_RESULT_CHANGED_AND_RELOAD)
			{
				Setup(0);
			}
		}
	}
	// Pan the camera based on keyboard cursor keys
	if (mission)
	{
		if (KeyIsDown(&gEventHandlers.keyboard, SDLK_LEFT))
		{
			camera.x -= CAMERA_PAN_SPEED;
			result.Redraw = result.RemakeBg = true;
		}
		else if (KeyIsDown(&gEventHandlers.keyboard, SDLK_RIGHT))
		{
			camera.x += CAMERA_PAN_SPEED;
			result.Redraw = result.RemakeBg = true;
		}
		if (KeyIsDown(&gEventHandlers.keyboard, SDLK_UP))
		{
			camera.y -= CAMERA_PAN_SPEED;
			result.Redraw = result.RemakeBg = true;
		}
		else if (KeyIsDown(&gEventHandlers.keyboard, SDLK_DOWN))
		{
			camera.y += CAMERA_PAN_SPEED;
			result.Redraw = result.RemakeBg = true;
		}
		// Also pan the camera based on middle mouse drag
		if (MouseIsDown(&gEventHandlers.mouse, SDL_BUTTON_MIDDLE))
		{
			camera = Vec2iAdd(camera, Vec2iMinus(
				gEventHandlers.mouse.previousPos,
				gEventHandlers.mouse.currentPos));
			result.Redraw = result.RemakeBg = true;
		}
		camera.x = CLAMP(camera.x, 0, Vec2iCenterOfTile(mission->Size).x);
		camera.y = CLAMP(camera.y, 0, Vec2iCenterOfTile(mission->Size).y);
	}
	bool hasQuit = false;
	if (gEventHandlers.keyboard.modState & (KMOD_ALT | KMOD_CTRL))
	{
		result.Redraw = true;
		switch (c)
		{
		case 'z':
			// Undo
			// Do this by swapping the current mission with the last mission
			// This requires a bit of copy-acrobatics; because missions
			// are saved in Setup(), but by this stage the mission has already
			// changed, _two_ mission caches are used, copied in sequence.
			// That is, if the current mission is at state B, the first cache
			// is still at state B (copied after the mission has changed
			// already), and the second cache is at state A.
			// If we were to perform an undo and still maintain functionality,
			// we need to copy such that the states change from B,B,A to
			// A,A,B.

			// However! The above is true only if we have "baked" changes
			// The editor has been optimised to perform some changes
			// without reloading map files; that is, the files are actually
			// in states C,B,A.
			// In this case, another set of "acrobatics" is required
			if (sHasUnbakedChanges)
			{
				MissionCopy(&lastMission, mission);	// B,A,Z -> B,A,B
			}
			else
			{
				MissionCopy(mission, &lastMission);	// B,B,A -> A,B,A
				MissionCopy(&lastMission, &currentMission);	// A,B,A -> A,B,B
			}
			fileChanged = 1;
			Setup(0);	// A,B,B -> A,A,B
			break;

		case 'x':
			MissionTerminate(scrap);
			MissionCopy(scrap, mission);
			Delete(*xc, *yc);
			break;

		case 'c':
			MissionTerminate(scrap);
			MissionCopy(scrap, mission);
			break;

		case 'v':
			// Use map size as a proxy to whether there's a valid scrap mission
			if (!Vec2iEqual(scrap->Size, Vec2iZero()))
			{
				InsertMission(&gCampaign, scrap, gCampaign.MissionIndex);
				fileChanged = 1;
				Setup(0);
			}
			break;

		case 'q':
			hasQuit = true;
			break;

		case 'n':
			InsertMission(&gCampaign, NULL, gCampaign.Setting.Missions.size);
			gCampaign.MissionIndex = gCampaign.Setting.Missions.size - 1;
			fileChanged = 1;
			Setup(0);
			break;
				
		case 'o':
			if (!fileChanged || ConfirmScreen(
				"File has been modified, but not saved", "Open anyway? (Y/N)"))
			{
				Open();
			}
			break;

		case 's':
			Save();
			break;

		case 'm':
			result.WillDisplayAutomap = true;
			break;

		case 'e':
			EditCharacters(&gCampaign.Setting);
			Setup(0);
			UIObjectUnhighlight(sObjs);
			CArrayTerminate(&sDrawObjs);
			break;
		}
	}
	else
	{
		if (c != 0)
		{
			result.Redraw = true;
		}
		switch (c)
		{
		case SDLK_F1:
			HelpScreen();
			break;

		case SDLK_HOME:
			if (gCampaign.MissionIndex > 0)
			{
				gCampaign.MissionIndex--;
			}
			Setup(0);
			break;

		case SDLK_END:
			if (gCampaign.MissionIndex < (int)gCampaign.Setting.Missions.size)
			{
				gCampaign.MissionIndex++;
			}
			Setup(0);
			break;

		case SDLK_INSERT:
			switch (*yc)
			{
			case YC_CHARACTERS:
				if (gCampaign.Setting.characters.OtherChars.size > 0)
				{
					int ch = 0;
					CArrayPushBack(&mission->Enemies, &ch);
					CharacterStoreAddBaddie(&gCampaign.Setting.characters, ch);
					*xc = mission->Enemies.size - 1;
				}
				break;

			case YC_SPECIALS:
				if (gCampaign.Setting.characters.OtherChars.size > 0)
				{
					int ch = 0;
					CArrayPushBack(&mission->SpecialChars, &ch);
					CharacterStoreAddSpecial(&gCampaign.Setting.characters, ch);
					*xc = mission->SpecialChars.size - 1;
				}
				break;

			case YC_ITEMS:
				{
					int item = 0;
					CArrayPushBack(&mission->Items, &item);
					CArrayPushBack(&mission->ItemDensities, &item);
					*xc = mission->Items.size - 1;
				}
				break;

			default:
				if (*yc >= YC_OBJECTIVES)
				{
					AddObjective(mission);
				}
				else
				{
					InsertMission(&gCampaign, NULL, gCampaign.MissionIndex);
				}
				break;
			}
			fileChanged = 1;
			Setup(0);
			break;

		case SDLK_DELETE:
			Delete(*xc, *yc);
			break;

		case SDLK_PAGEUP:
			if (Change(o, *yc, 1))
			{
				fileChanged = 1;
			}
			Setup(0);
			break;

		case SDLK_PAGEDOWN:
			if (Change(o, *yc, -1))
			{
				fileChanged = 1;
			}
			Setup(0);
			break;

		case SDLK_ESCAPE:
			hasQuit = true;
			break;

		case SDLK_BACKSPACE:
			fileChanged |= UIObjectDelChar(sObjs);
			break;

		default:
			c = KeyGetTyped(&gEventHandlers.keyboard);
			if (c)
			{
				fileChanged |= UIObjectAddChar(sObjs, (char)c);
			}
			break;
		}
	}
	if (gEventHandlers.HasQuit)
	{
		hasQuit = true;
	}
	if (hasQuit && (!fileChanged || ConfirmScreen(
		"File has been modified, but not saved", "Quit anyway? (Y/N)")))
	{
		result.Done = true;
	}
	return result;
}
Ejemplo n.º 2
0
EditorResult EditorBrushStopPainting(EditorBrush *b, Mission *m)
{
	EditorResult result = EDITOR_RESULT_NONE;
	if (b->IsPainting)
	{
		switch (b->Type)
		{
		case BRUSHTYPE_LINE:
			EditorBrushPaintLine(b, m);
			result = EDITOR_RESULT_CHANGED;
			break;
		case BRUSHTYPE_BOX:
			EditorBrushPaintBox(b, m, b->PaintType, MAP_UNSET);
			result = EDITOR_RESULT_CHANGED;
			break;
		case BRUSHTYPE_BOX_FILLED:
			EditorBrushPaintBox(b, m, b->PaintType, b->PaintType);
			result = EDITOR_RESULT_CHANGED;
			break;
		case BRUSHTYPE_ROOM:
			EditorBrushPaintBox(b, m, MAP_WALL, MAP_ROOM);
			result = EDITOR_RESULT_CHANGED;
			break;
		case BRUSHTYPE_ROOM_PAINTER:
			// Reload map to update tiles
			result = EDITOR_RESULT_RELOAD;
			break;
		case BRUSHTYPE_SELECT:
			if (b->IsMoving)
			{
				// Move the tiles from the source to the target
				// Need to copy all the tiles to a temp buffer first in case
				// we are moving to an overlapped position
				CArray movedTiles;
				Vec2i v;
				int i;
				int delta;
				CArrayInit(&movedTiles, sizeof(unsigned short));
				// Copy tiles to temp from selection, setting them to MAP_FLOOR
				// in the process
				for (v.y = 0; v.y < b->SelectionSize.y; v.y++)
				{
					for (v.x = 0; v.x < b->SelectionSize.x; v.x++)
					{
						Vec2i vOffset = Vec2iAdd(v, b->SelectionStart);
						int idx = vOffset.y * m->Size.x + vOffset.x;
						unsigned short *tile = CArrayGet(
							&m->u.Static.Tiles, idx);
						CArrayPushBack(&movedTiles, tile);
						*tile = MAP_FLOOR;
					}
				}
				// Move the selection to the new position
				b->SelectionStart.x += b->Pos.x - b->DragPos.x;
				b->SelectionStart.y += b->Pos.y - b->DragPos.y;
				// Copy tiles to the new area, for parts of the new area that
				// are valid
				i = 0;
				for (v.y = 0; v.y < b->SelectionSize.y; v.y++)
				{
					for (v.x = 0; v.x < b->SelectionSize.x; v.x++)
					{
						Vec2i vOffset = Vec2iAdd(v, b->SelectionStart);
						if (vOffset.x >= 0 && vOffset.x < m->Size.x &&
							vOffset.y >= 0 && vOffset.y < m->Size.y)
						{
							int idx = vOffset.y * m->Size.x + vOffset.x;
							unsigned short *tileFrom =
								CArrayGet(&movedTiles, i);
							unsigned short *tileTo = CArrayGet(
								&m->u.Static.Tiles, idx);
							*tileTo = *tileFrom;
							result = EDITOR_RESULT_CHANGED_AND_RELOAD;
						}
						i++;
					}
				}
				// Update the selection to fit within map boundaries
				delta = -b->SelectionStart.x;
				if (delta > 0)
				{
					b->SelectionStart.x += delta;
					b->SelectionSize.x -= delta;
				}
				delta = -b->SelectionStart.y;
				if (delta > 0)
				{
					b->SelectionStart.y += delta;
					b->SelectionSize.y -= delta;
				}
				delta = b->SelectionStart.x + b->SelectionSize.x - m->Size.x;
				if (delta > 0)
				{
					b->SelectionSize.x -= delta;
				}
				delta = b->SelectionStart.y + b->SelectionSize.y - m->Size.y;
				if (delta > 0)
				{
					b->SelectionSize.y -= delta;
				}
				// Check if the selection is still valid; if not, invalidate it
				if (b->SelectionSize.x < 0 || b->SelectionSize.y < 0)
				{
					b->SelectionSize = Vec2iZero();
				}

				b->IsMoving = 0;
			}
			else
			{
				// Record the selection size
				b->SelectionStart = Vec2iMin(b->LastPos, b->Pos);
				b->SelectionSize.x = abs(b->LastPos.x - b->Pos.x) + 1;
				b->SelectionSize.y = abs(b->LastPos.y - b->Pos.y) + 1;
				// Disallow 1x1 selection sizes
				if (b->SelectionSize.x <= 1 && b->SelectionSize.y <= 1)
				{
					b->SelectionSize = Vec2iZero();
				}
			}
			break;
		case BRUSHTYPE_SET_EXIT:
			{
				Vec2i exitStart = Vec2iMin(b->LastPos, b->Pos);
				Vec2i exitEnd = Vec2iMax(b->LastPos, b->Pos);
				// Clamp within map boundaries
				exitStart = Vec2iClamp(
					exitStart, Vec2iZero(), Vec2iMinus(m->Size, Vec2iUnit()));
				exitEnd = Vec2iClamp(
					exitEnd, Vec2iZero(), Vec2iMinus(m->Size, Vec2iUnit()));
				// Check that size is big enough
				Vec2i size =
					Vec2iAdd(Vec2iMinus(exitEnd, exitStart), Vec2iUnit());
				if (size.x >= 3 && size.y >= 3)
				{
					// Check that exit area has changed
					if (!Vec2iEqual(exitStart, m->u.Static.Exit.Start) ||
						!Vec2iEqual(exitEnd, m->u.Static.Exit.End))
					{
						m->u.Static.Exit.Start = exitStart;
						m->u.Static.Exit.End = exitEnd;
						result = EDITOR_RESULT_CHANGED_AND_RELOAD;
					}
				}
			}
			break;
		default:
			// do nothing
			break;
		}
	}
	b->IsPainting = 0;
	CArrayClear(&b->HighlightedTiles);
	return result;
}