Exemple #1
0
static void DrawEditorTiles(DrawBuffer *b, Vec2i offset)
{
	Vec2i pos;
	Tile *tile = &b->tiles[0][0];
	pos.y = b->dy + offset.y;
	for (int y = 0; y < Y_TILES; y++, pos.y += TILE_HEIGHT)
	{
		pos.x = b->dx + offset.x;
		for (int x = 0; x < b->Size.x; x++, tile++, pos.x += TILE_WIDTH)
		{
			if (gMission.missionData->Type == MAPTYPE_STATIC)
			{
				Vec2i start = gMission.missionData->u.Static.Start;
				if (!Vec2iEqual(start, Vec2iZero()) &&
					Vec2iEqual(start, Vec2iNew(x + b->xStart, y + b->yStart)))
				{
					// mission start
					BlitMasked(
						&gGraphicsDevice,
						PicManagerGetPic(&gPicManager, "editor/start"),
						pos, colorWhite, 1);
				}
			}
		}
		tile += X_TILES - b->Size.x;
	}
}
Exemple #2
0
int AIGoto(TActor *actor, Vec2i p, bool ignoreObjects)
{
    Vec2i a = Vec2iFull2Real(actor->Pos);
    Vec2i currentTile = Vec2iToTile(a);
    Vec2i goalTile = Vec2iToTile(p);
    AIGotoContext *c = &actor->aiContext->Goto;

    // If we are already there, bail
    // This can happen if AI is trying to track the player,
    // but the player has died, for example.
    if (Vec2iEqual(currentTile, goalTile))
    {
        return 0;
    }

    // If we are currently following an A* path,
    // and it is still valid, keep following it until
    // we have reached a new tile
    if (c && c->IsFollowing && AStarCloseToPath(c, currentTile, goalTile))
    {
        return AStarFollow(c, currentTile, &actor->tileItem, a);
    }
    else if (AIHasClearPath(a, p, ignoreObjects))
    {
        // Simple case: if there's a clear line between AI and target,
        // walk straight towards it
        return AIGotoDirect(a, p);
    }
    else
    {
        // We need to recalculate A*

        AStarContext ac;
        ac.Map = &gMap;
        ac.IsTileOk =
            ignoreObjects ? IsTileWalkable : IsTileWalkableAroundObjects;
        // First, if the goal tile is blocked itself,
        // find a nearby tile that can be walked to
        c->Goal = MapSearchTileAround(ac.Map, goalTile, ac.IsTileOk);

        c->PathIndex = 1;	// start navigating to the next path node
        ASPathDestroy(c->Path);
        c->Path = ASPathCreate(
                      &cPathNodeSource, &ac, &currentTile, &c->Goal);

        // In case we can't calculate A* for some reason,
        // try simple navigation again
        if (ASPathGetCount(c->Path) <= 1)
        {
            debug(
                D_MAX,
                "Error: can't calculate path from {%d, %d} to {%d, %d}",
                currentTile.x, currentTile.y,
                goalTile.x, goalTile.y);
            return AIGotoDirect(a, p);
        }

        return AStarFollow(c, currentTile, &actor->tileItem, a);
    }
}
Exemple #3
0
static void MakeBackground(GraphicsDevice *g, int buildTables)
{
	if (buildTables)
	{
		// Automatically pan camera to middle of screen
		Mission *m = gMission.missionData;
		Vec2i focusTile = Vec2iScaleDiv(m->Size, 2);
		// Better yet, if the map has a known start position, focus on that
		if (m->Type == MAPTYPE_STATIC &&
			!Vec2iEqual(m->u.Static.Start, Vec2iZero()))
		{
			focusTile = m->u.Static.Start;
		}
		camera = Vec2iCenterOfTile(focusTile);
	}

	// Clear background first
	for (int i = 0; i < GraphicsGetScreenSize(&g->cachedConfig); i++)
	{
		g->buf[i] = PixelFromColor(g, colorBlack);
	}
	GrafxDrawExtra extra;
	extra.guideImage = brush.GuideImageSurface;
	extra.guideImageAlpha = brush.GuideImageAlpha;

	DrawBufferTerminate(&sDrawBuffer);
	DrawBufferInit(&sDrawBuffer, Vec2iNew(X_TILES, Y_TILES), &gGraphicsDevice);
	GrafxMakeBackground(
		g, &sDrawBuffer, &gCampaign, &gMission, &gMap,
		tintNone, 1, buildTables, camera, &extra);
}
Exemple #4
0
void UIButtonSetPic(UIObject *o, Pic *pic)
{
	assert(o->Type == UITYPE_BUTTON && "invalid UI type");
	o->u.Button.Pic = pic;
	if (Vec2iEqual(o->Size, Vec2iZero()))
	{
		o->Size = o->u.Button.Pic->size;
	}
}
Exemple #5
0
// Follow the current A* path
static int AStarFollow(
    AIGotoContext *c, Vec2i currentTile, TTileItem *i, Vec2i a)
{
    Vec2i *pathTile = ASPathGetNode(c->Path, c->PathIndex);
    c->IsFollowing = 1;
    // Check if we need to follow the next step in the path
    // Note: need to make sure the actor is fully within the current tile
    // otherwise it may get stuck at corners
    if (Vec2iEqual(currentTile, *pathTile) &&
            IsTileItemInsideTile(i, currentTile))
    {
        c->PathIndex++;
        pathTile = ASPathGetNode(c->Path, c->PathIndex);
        c->IsFollowing = 0;
    }
    // Go directly to the center of the next tile
    return AIGotoDirect(a, Vec2iCenterOfTile(*pathTile));
}
Exemple #6
0
void GraphicsConfigSet(
	GraphicsConfig *c,
	const Vec2i res, const bool fullscreen, const int scaleFactor)
{
	if (!Vec2iEqual(res, c->Res))
	{
		c->Res = res;
		c->needRestart = true;
	}
#define SET(_lhs, _rhs) \
	if ((_lhs) != (_rhs)) \
	{ \
		(_lhs) = (_rhs); \
		c->needRestart = true; \
	}
	SET(c->Fullscreen, fullscreen);
	SET(c->ScaleFactor, scaleFactor);
}
Exemple #7
0
void GraphicsConfigSet(
    GraphicsConfig *c,
    const Vec2i res, const bool fullscreen,
    const int scaleFactor, const ScaleMode scaleMode, const int brightness)
{
    if (!Vec2iEqual(res, c->Res))
    {
        c->Res = res;
        c->RestartFlags |= RESTART_RESOLUTION;
    }
#define SET(_lhs, _rhs, _flag) \
	if ((_lhs) != (_rhs)) \
	{ \
		(_lhs) = (_rhs); \
		c->RestartFlags |= (_flag); \
	}
    SET(c->Fullscreen, fullscreen, RESTART_RESOLUTION);
    SET(c->ScaleFactor, scaleFactor, RESTART_RESOLUTION);
    SET(c->ScaleMode, scaleMode, RESTART_SCALE_MODE);
    SET(c->Brightness, brightness, RESTART_BRIGHTNESS);
}
static bool TryRemoveMapObjectAt(const Vec2i pos, CArray *objs)
{
	for (int i = 0; i < (int)objs->size; i++)
	{
		MapObjectPositions *mop = CArrayGet(objs, i);
		for (int j = 0; j < (int)mop->Positions.size; j++)
		{
			const Vec2i *mopPos = CArrayGet(&mop->Positions, j);
			if (Vec2iEqual(*mopPos, pos))
			{
				CArrayDelete(&mop->Positions, j);
				if (mop->Positions.size == 0)
				{
					CArrayTerminate(&mop->Positions);
					CArrayDelete(objs, i);
				}
				return true;
			}
		}
	}
	return false;
}
Exemple #9
0
static Vec2i SeekTowards(
	const Vec2i pos, const Vec2i vel, const double speedMin,
	const Vec2i targetPos, const int seekFactor)
{
	// Compensate for bullet's velocity
	const Vec2i targetVel = Vec2iMinus(Vec2iMinus(targetPos, pos), vel);
	// Don't seek if the coordinates are too big
	if (abs(targetVel.x) > 10000 || abs(targetVel.y) > 10000 ||
		Vec2iIsZero(targetVel))
	{
		return vel;
	}
	const double targetMag = sqrt(
		targetVel.x*targetVel.x + targetVel.y*targetVel.y);
	const double magnitude = MAX(speedMin,
		Vec2iEqual(vel, Vec2iZero()) ? speedMin : sqrt(vel.x*vel.x + vel.y*vel.y));
	const double combinedX =
		vel.x / magnitude * seekFactor + targetVel.x / targetMag;
	const double combinedY =
		vel.y / magnitude * seekFactor + targetVel.y / targetMag;
	return Vec2iNew(
		(int)round(combinedX * magnitude / (seekFactor + 1)),
		(int)round(combinedY * magnitude / (seekFactor + 1)));
}
Exemple #10
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;
}
Exemple #11
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;
}
Exemple #12
0
int MouseHasMoved(Mouse *mouse)
{
	return !Vec2iEqual(mouse->previousPos, mouse->currentPos);
}