static float AStarHeuristic(void *fromNode, void *toNode, void *context) { // Simple Euclidean Vec2i *v1 = fromNode; Vec2i *v2 = toNode; UNUSED(context); return (float)sqrt(DistanceSquared( Vec2iCenterOfTile(*v1), Vec2iCenterOfTile(*v2))); }
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); }
void GrafxMakeRandomBackground( GraphicsDevice *device, CampaignOptions *co, struct MissionOptions *mo, Map *map) { HSV tint; CampaignSettingInit(&co->Setting); ActorsInit(); ObjsInit(); MobObjsInit(); SetupQuickPlayCampaign(&co->Setting, &gConfig.QuickPlay); co->seed = rand(); tint.h = rand() * 360.0 / RAND_MAX; tint.s = rand() * 1.0 / RAND_MAX; tint.v = 0.5; DrawBuffer buffer; DrawBufferInit(&buffer, Vec2iNew(X_TILES, Y_TILES), device); co->MissionIndex = 0; GrafxMakeBackground( device, &buffer, co, mo, map, tint, 0, 1, Vec2iCenterOfTile(Vec2iScaleDiv(map->Size, 2)), NULL); DrawBufferTerminate(&buffer); ActorsTerminate(); ObjsTerminate(); MobObjsTerminate(); RemoveAllWatches(); MissionOptionsTerminate(mo); CampaignSettingTerminate(&co->Setting); co->seed = gConfig.Game.RandomSeed; }
Vec2i MapObjectGetPlacementPos(const MapObject *mo, const Vec2i tilePos) { Vec2i pos = Vec2iCenterOfTile(tilePos); pos = Vec2iAdd(pos, mo->PosOffset); // For on-wall objects, set their position to the top of the tile // This guarantees that they are drawn last if (mo->Flags & (1 << PLACEMENT_ON_WALL)) { pos.y -= TILE_HEIGHT / 2 + 1; } return pos; }
// 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)); }
Vec2i PlacePlayer( Map *map, const PlayerData *p, const Vec2i firstPos, const bool pumpEvents) { NetMsgActorAdd aa = NetMsgActorAdd_init_default; aa.Id = ActorsGetFreeIndex(); aa.Health = p->Char.maxHealth; aa.PlayerId = p->playerIndex; if (IsPVP(gCampaign.Entry.Mode)) { // In a PVP mode, always place players apart aa.FullPos = PlaceActor(&gMap); } else if (gMission.missionData->Type == MAPTYPE_STATIC && !Vec2iIsZero(gMission.missionData->u.Static.Start)) { // place players near the start point Vec2i startPoint = Vec2iReal2Full(Vec2iCenterOfTile( gMission.missionData->u.Static.Start)); aa.FullPos = PlaceActorNear(map, startPoint, true); } else if (gConfig.Interface.Splitscreen == SPLITSCREEN_NEVER && !Vec2iIsZero(firstPos)) { // If never split screen, try to place players near the first player aa.FullPos = PlaceActorNear(map, firstPos, true); } else { aa.FullPos = PlaceActor(map); } GameEvent e = GameEventNew(GAME_EVENT_ACTOR_ADD); e.u.ActorAdd = aa; GameEventsEnqueue(&gGameEvents, e); if (pumpEvents) { // Process the events that actually place the players HandleGameEvents(&gGameEvents, NULL, NULL, NULL, &gEventHandlers); } return Vec2iNew(aa.FullPos.x, aa.FullPos.y); }
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, ¤tMission); // 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; }