int CanCompleteMission(struct MissionOptions *options) { int i; // Death is the only escape from dogfights and quick play if (gCampaign.Entry.Mode == CAMPAIGN_MODE_DOGFIGHT) { return GetNumPlayersAlive() <= 1; } else if (gCampaign.Entry.Mode == CAMPAIGN_MODE_QUICK_PLAY) { return GetNumPlayersAlive() == 0; } // Check all objective counts are enough for (i = 0; i < (int)options->Objectives.size; i++) { struct Objective *o = CArrayGet(&options->Objectives, i); MissionObjective *mobj = CArrayGet(&options->missionData->Objectives, i); if (o->done < mobj->Required) { return 0; } } return 1; }
void HUDNumPopupsDrawObjective( const HUDNumPopups *popups, const int idx, const Vec2i pos) { const Objective *o = CArrayGet(&gMission.missionData->Objectives, idx); const HUDNumPopup *p = CArrayGet(&popups->objective, idx); DrawNumUpdate(p, "%d", o->done, pos, 0); }
static HitType GetHitType( const TTileItem *ti, const TMobileObject *bullet, int *targetUID) { *targetUID = -1; HitType ht = HIT_NONE; switch (ti->kind) { case KIND_CHARACTER: ht = HIT_FLESH; *targetUID = ((const TActor *)CArrayGet(&gActors, ti->id))->uid; break; case KIND_OBJECT: ht = HIT_OBJECT; *targetUID = ((const TObject *)CArrayGet(&gObjs, ti->id))->uid; break; default: CASSERT(false, "cannot damage target kind"); break; } if (bullet->tileItem.SoundLock > 0 || !HasHitSound( bullet->bulletClass->Power, bullet->flags, bullet->PlayerUID, ti->kind, *targetUID, bullet->bulletClass->Special, true)) { ht = HIT_NONE; } return ht; }
static void DrawObjectiveHighlight( TTileItem *ti, Tile *tile, DrawBuffer *b, Vec2i offset) { color_t color; if (ti->flags & TILEITEM_OBJECTIVE) { // Objective const int objective = ObjectiveFromTileItem(ti->flags); const Objective *o = CArrayGet(&gMission.missionData->Objectives, objective); if (o->Flags & OBJECTIVE_HIDDEN) { return; } if (!(o->Flags & OBJECTIVE_POSKNOWN) && (tile->flags & MAPTILE_OUT_OF_SIGHT)) { return; } color = o->color; } else if (ti->kind == KIND_PICKUP) { // Gun pickup const Pickup *p = CArrayGet(&gPickups, ti->id); if (!PickupIsManual(p)) { return; } color = colorDarker; } else { return; } const Vec2i pos = Vec2iNew( ti->x - b->xTop + offset.x, ti->y - b->yTop + offset.y); const int pulsePeriod = ConfigGetInt(&gConfig, "Game.FPS"); int alphaUnscaled = (gMission.time % pulsePeriod) * 255 / (pulsePeriod / 2); if (alphaUnscaled > 255) { alphaUnscaled = 255 * 2 - alphaUnscaled; } color.a = (Uint8)alphaUnscaled; if (ti->getPicFunc != NULL) { Vec2i picOffset; const Pic *pic = ti->getPicFunc(ti->id, &picOffset); BlitPicHighlight( &gGraphicsDevice, pic, Vec2iAdd(pos, picOffset), color); } else if (ti->kind == KIND_CHARACTER) { TActor *a = CArrayGet(&gActors, ti->id); ActorPics pics = GetCharacterPicsFromActor(a); DrawActorHighlight(&pics, pos, color); } }
static void DrawObjectiveInfo( const struct MissionOptions *mo, const int idx, const Vec2i pos) { const MissionObjective *mobj = CArrayGet(&mo->missionData->Objectives, idx); const ObjectiveDef *o = CArrayGet(&mo->Objectives, idx); const CharacterStore *store = &gCampaign.Setting.characters; switch (mobj->Type) { case OBJECTIVE_KILL: { const Character *cd = CArrayGet( &store->OtherChars, CharacterStoreGetSpecialId(store, 0)); const int i = cd->looks.face; TOffsetPic pic; pic.picIndex = cHeadPic[i][DIRECTION_DOWN][STATE_IDLE]; pic.dx = cHeadOffset[i][DIRECTION_DOWN].dx; pic.dy = cHeadOffset[i][DIRECTION_DOWN].dy; DrawTTPic( pos.x + pic.dx, pos.y + pic.dy, PicManagerGetOldPic(&gPicManager, pic.picIndex), &cd->table); } break; case OBJECTIVE_RESCUE: { const Character *cd = CArrayGet( &store->OtherChars, CharacterStoreGetPrisonerId(store, 0)); const int i = cd->looks.face; TOffsetPic pic; pic.picIndex = cHeadPic[i][DIRECTION_DOWN][STATE_IDLE]; pic.dx = cHeadOffset[i][DIRECTION_DOWN].dx; pic.dy = cHeadOffset[i][DIRECTION_DOWN].dy; DrawTTPic( pos.x + pic.dx, pos.y + pic.dy, PicManagerGetOldPic(&gPicManager, pic.picIndex), &cd->table); } break; case OBJECTIVE_COLLECT: { const Pic *p = o->pickupClass->Pic; Blit(&gGraphicsDevice, p, Vec2iAdd(pos, p->offset)); } break; case OBJECTIVE_DESTROY: { Vec2i picOffset; const Pic *p = MapObjectGetPic(IntMapObject(mobj->Index), &picOffset, false); Blit(&gGraphicsDevice, p, Vec2iAdd(pos, picOffset)); } break; case OBJECTIVE_INVESTIGATE: // Don't draw return; default: CASSERT(false, "Unknown objective type"); return; } }
void UIObjectDraw( UIObject *o, GraphicsDevice *g, Vec2i pos, Vec2i mouse, CArray *drawObjs) { // Draw this UIObject and its children in BFS order // Maintain a queue of UIObjects to draw if (drawObjs->elemSize == 0) { CArrayInit(drawObjs, sizeof(UIObjectDrawContext)); UIObjectDrawContext c; c.obj = o; c.pos = pos; CArrayPushBack(drawObjs, &c); for (int i = 0; i < (int)drawObjs->size; i++) { UIObjectDrawContext *cPtr = CArrayGet(drawObjs, i); UIObjectDrawAndAddChildren( cPtr->obj, g, cPtr->pos, mouse, drawObjs); } } else { for (int i = 0; i < (int)drawObjs->size; i++) { UIObjectDrawContext *cPtr = CArrayGet(drawObjs, i); UIObjectDrawAndAddChildren( cPtr->obj, g, cPtr->pos, mouse, NULL); } } }
TTileItem *ThingIdGetTileItem(ThingId *tid) { TTileItem *ti = NULL; switch (tid->Kind) { case KIND_CHARACTER: ti = &((TActor *)CArrayGet(&gActors, tid->Id))->tileItem; break; case KIND_PARTICLE: ti = &((Particle *)CArrayGet(&gParticles, tid->Id))->tileItem; break; case KIND_MOBILEOBJECT: ti = &((TMobileObject *)CArrayGet( &gMobObjs, tid->Id))->tileItem; break; case KIND_OBJECT: ti = &((TObject *)CArrayGet(&gObjs, tid->Id))->tileItem; break; case KIND_PICKUP: ti = &((Pickup *)CArrayGet(&gPickups, tid->Id))->tileItem; break; default: CASSERT(false, "unknown tile item to get"); break; } return ti; }
static bool IsTileWalkable(Map *map, Vec2i pos) { if (!IsTileWalkableOrOpenable(map, pos)) { return false; } // Check if tile has a dangerous (explosive) item on it // For AI, we don't want to shoot it, so just walk around Tile *t = MapGetTile(map, pos); for (int i = 0; i < (int)t->things.size; i++) { ThingId *tid = CArrayGet(&t->things, i); // Only look for explosive objects if (tid->Kind != KIND_OBJECT) { continue; } TObject *o = CArrayGet(&gObjs, tid->Id); if (o->flags & OBJFLAG_DANGEROUS) { return false; } } return true; }
static void SetupBadguysForMission(Mission *mission) { int i; CharacterStore *s = &gCampaign.Setting.characters; CharacterStoreResetOthers(s); if (s->OtherChars.size == 0) { return; } for (i = 0; i < (int)mission->Objectives.size; i++) { MissionObjective *mobj = CArrayGet(&mission->Objectives, i); if (mobj->Type == OBJECTIVE_RESCUE) { CharacterStoreAddPrisoner(s, mobj->Index); break; // TODO: multiple prisoners } } for (i = 0; i < (int)mission->Enemies.size; i++) { CharacterStoreAddBaddie(s, *(int *)CArrayGet(&mission->Enemies, i)); } for (i = 0; i < (int)mission->SpecialChars.size; i++) { CharacterStoreAddSpecial( s, *(int *)CArrayGet(&mission->SpecialChars, i)); } }
bool PlayerSelection(void) { PlayerSelectionData data; memset(&data, 0, sizeof data); data.IsOK = true; GetDataFilePath(data.prefixes, "data/prefixes.txt"); GetDataFilePath(data.suffixes, "data/suffixes.txt"); GetDataFilePath(data.suffixnames, "data/suffixnames.txt"); NameGenInit(&data.g, data.prefixes, data.suffixes, data.suffixnames); // Create selection menus for each local player for (int i = 0, idx = 0; i < (int)gPlayerDatas.size; i++, idx++) { PlayerData *p = CArrayGet(&gPlayerDatas, i); if (!p->IsLocal) { idx--; continue; } PlayerSelectMenusCreate( &data.menus[idx], GetNumPlayers(false, false, true), idx, i, &gEventHandlers, &gGraphicsDevice, &data.g); } if (!gCampaign.IsClient) { NetServerOpen(&gNetServer); } GameLoopData gData = GameLoopDataNew( &data, PlayerSelectionUpdate, &data, PlayerSelectionDraw); GameLoop(&gData); if (data.IsOK) { for (int i = 0, idx = 0; i < (int)gPlayerDatas.size; i++, idx++) { PlayerData *p = CArrayGet(&gPlayerDatas, i); if (!p->IsLocal) { idx--; continue; } // For any player slots not picked, turn them into AIs if (p->inputDevice == INPUT_DEVICE_UNSET) { PlayerSetInputDevice(p, INPUT_DEVICE_AI, 0); } } } for (int i = 0; i < GetNumPlayers(false, false, true); i++) { MenuSystemTerminate(&data.menus[i].ms); } NameGenTerminate(&data.g); return data.IsOK; }
static void DrawObjectiveHighlight( TTileItem *ti, Tile *tile, DrawBuffer *b, Vec2i offset) { if (!(ti->flags & TILEITEM_OBJECTIVE)) { return; } int objective = ObjectiveFromTileItem(ti->flags); MissionObjective *mo = CArrayGet(&gMission.missionData->Objectives, objective); if (mo->Flags & OBJECTIVE_HIDDEN) { return; } if (!(mo->Flags & OBJECTIVE_POSKNOWN) && (tile->flags & MAPTILE_OUT_OF_SIGHT)) { return; } Vec2i pos = Vec2iNew( ti->x - b->xTop + offset.x, ti->y - b->yTop + offset.y); struct Objective *o = CArrayGet(&gMission.Objectives, objective); color_t color = o->color; int pulsePeriod = FPS_FRAMELIMIT; int alphaUnscaled = (gMission.time % pulsePeriod) * 255 / (pulsePeriod / 2); if (alphaUnscaled > 255) { alphaUnscaled = 255 * 2 - alphaUnscaled; } color.a = (Uint8)alphaUnscaled; if (ti->getPicFunc != NULL) { Vec2i picOffset; const Pic *pic = ti->getPicFunc(ti->id, &picOffset); BlitPicHighlight( &gGraphicsDevice, pic, Vec2iAdd(pos, picOffset), color); } else if (ti->getActorPicsFunc != NULL) { ActorPics pics = ti->getActorPicsFunc(ti->id); // Do not highlight dead, dying or transparent characters if (!pics.IsDead && !pics.IsTransparent) { for (int i = 0; i < 3; i++) { if (PicIsNotNone(&pics.Pics[i])) { BlitPicHighlight( &gGraphicsDevice, &pics.Pics[i], pos, color); } } } } }
static void DogfightFinalScoresDraw(void *data) { UNUSED(data); // This will only draw once const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; GraphicsBlitBkg(&gGraphicsDevice); // Work out who's the winner, or if it's a tie int maxScore = 0; int playersWithMaxScore = 0; for (int i = 0; i < (int)gPlayerDatas.size; i++) { const PlayerData *p = CArrayGet(&gPlayerDatas, i); if (p->RoundsWon > maxScore) { maxScore = p->RoundsWon; playersWithMaxScore = 1; } else if (p->RoundsWon == maxScore) { playersWithMaxScore++; } } const bool isTie = playersWithMaxScore == (int)gPlayerDatas.size; // Draw players and their names spread evenly around the screen. // If it's a tie, display the message in the centre, // otherwise display the winner just below the winning player #define DRAW_TEXT "It's a draw!" #define WINNER_TEXT "Winner!" CASSERT( gPlayerDatas.size >= 2 && gPlayerDatas.size <= 4, "Unimplemented number of players for dogfight"); for (int i = 0; i < (int)gPlayerDatas.size; i++) { const Vec2i pos = Vec2iNew( w / 4 + (i & 1) * w / 2, gPlayerDatas.size == 2 ? h / 2 : h / 4 + (i / 2) * h / 2); const PlayerData *p = CArrayGet(&gPlayerDatas, i); DisplayCharacterAndName(pos, &p->Char, p->name); ShowPlayerScore(pos, p->RoundsWon); if (!isTie && maxScore == p->RoundsWon) { FontStrMask( WINNER_TEXT, Vec2iNew(pos.x - FontStrW(WINNER_TEXT) / 2, pos.y + 30), colorGreen); } } if (isTie) { FontStrCenter(DRAW_TEXT); } }
Ammo *AmmoGetById(AmmoClasses *ammo, const int id) { if (id < (int)ammo->Ammo.size) { return CArrayGet(&ammo->Ammo, id); } else { return CArrayGet(&ammo->CustomAmmo, id - (int)ammo->Ammo.size); } }
static void ActionRun(Action *a, CArray *mapTriggers) { int i; switch (a->action) { case ACTION_NULL: return; case ACTION_SOUND: SoundPlayAt(&gSoundDevice, a->a.Sound, a->u.pos); break; case ACTION_SETTRIGGER: for (i = 0; i < (int)mapTriggers->size; i++) { Trigger *tr = *(Trigger **)CArrayGet(mapTriggers, i); if (tr->id == a->u.index) { tr->isActive = 1; break; } } break; case ACTION_CLEARTRIGGER: for (i = 0; i < (int)mapTriggers->size; i++) { Trigger *tr = *(Trigger **)CArrayGet(mapTriggers, i); if (tr->id == a->u.index) { tr->isActive = 0; break; } } break; case ACTION_CHANGETILE: { Tile *t= MapGetTile(&gMap, a->u.pos); t->flags = a->a.tileFlags; t->pic = a->tilePic; t->picAlt = a->tilePicAlt; } break; case ACTION_ACTIVATEWATCH: ActivateWatch(a->u.index); break; case ACTION_DEACTIVATEWATCH: DeactivateWatch(a->u.index); break; } }
MapObject *IndexMapObject(const int i) { CASSERT( i >= 0 && i < (int)gMapObjects.Classes.size + (int)gMapObjects.CustomClasses.size, "Map object index out of bounds"); if (i < (int)gMapObjects.Classes.size) { return CArrayGet(&gMapObjects.Classes, i); } return CArrayGet(&gMapObjects.CustomClasses, i - gMapObjects.Classes.size); }
bool ScreenMissionSummary(CampaignOptions *c, struct MissionOptions *m) { // Save password MissionSave ms; MissionSaveInit(&ms); ms.Campaign = c->Entry; // Don't make password for next level if there is none int passwordIndex = m->index + 1; if (passwordIndex == c->Entry.NumMissions) { passwordIndex--; } strcpy(ms.Password, MakePassword(passwordIndex, 0)); ms.MissionsCompleted = m->index + 1; AutosaveAddMission(&gAutosave, &ms, ms.Campaign.BuiltinIndex); AutosaveSave(&gAutosave, GetConfigFilePath(AUTOSAVE_FILE)); // Calculate bonus scores // Bonuses only apply if at least one player has lived if (AreAnySurvived()) { int bonus = 0; // Objective bonuses for (int i = 0; i < (int)m->missionData->Objectives.size; i++) { const struct Objective *o = CArrayGet(&m->Objectives, i); const MissionObjective *mo = CArrayGet(&m->missionData->Objectives, i); if (o->done == mo->Count && o->done > mo->Required) { // Perfect bonus += PERFECT_BONUS; } } bonus += GetAccessBonus(m); bonus += GetTimeBonus(m, NULL); for (int i = 0; i < (int)gPlayerDatas.size; i++) { PlayerData *p = CArrayGet(&gPlayerDatas, i); ApplyBonuses(p, bonus); } } GameLoopWaitForAnyKeyOrButtonData wData; GameLoopData gData = GameLoopDataNew( &wData, GameLoopWaitForAnyKeyOrButtonFunc, m, MissionSummaryDraw); GameLoop(&gData); if (wData.IsOK) { SoundPlay(&gSoundDevice, StrSound("mg")); } return wData.IsOK; }
static void DeathmatchFinalScoresDraw(void *data) { UNUSED(data); // This will only draw once const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; GraphicsBlitBkg(&gGraphicsDevice); // Work out the highest kills int maxKills = 0; for (int i = 0; i < (int)gPlayerDatas.size; i++) { const PlayerData *p = CArrayGet(&gPlayerDatas, i); if (p->kills > maxKills) { maxKills = p->kills; } } // Draw players and their names spread evenly around the screen. CASSERT( gPlayerDatas.size >= 2 && gPlayerDatas.size <= 4, "Unimplemented number of players for deathmatch"); #define LAST_MAN_TEXT "Last man standing!" for (int i = 0; i < (int)gPlayerDatas.size; i++) { const Vec2i pos = Vec2iNew( w / 4 + (i & 1) * w / 2, gPlayerDatas.size == 2 ? h / 2 : h / 4 + (i / 2) * h / 2); const PlayerData *p = CArrayGet(&gPlayerDatas, i); DisplayCharacterAndName(pos, &p->Char, p->name); // Kills char s[16]; sprintf(s, "Kills: %d", p->kills); FontStrMask( s, Vec2iNew(pos.x - FontStrW(s) / 2, pos.y + 20), p->kills == maxKills ? colorGreen : colorWhite); // Last man standing? if (p->Lives > 0) { FontStrMask( LAST_MAN_TEXT, Vec2iNew(pos.x - FontStrW(LAST_MAN_TEXT) / 2, pos.y + 30), colorGreen); } } }
GunDescription *IdGunDescription(const int i) { CASSERT( i >= 0 && i < (int)gGunDescriptions.Guns.size + (int)gGunDescriptions.CustomGuns.size, "Gun index out of bounds"); if (i < (int)gGunDescriptions.Guns.size) { return CArrayGet(&gGunDescriptions.Guns, i); } return CArrayGet( &gGunDescriptions.CustomGuns, i - gGunDescriptions.Guns.size); }
void PicManagerClear(CArray *pics, CArray *sprites) { for (int i = 0; i < (int)pics->size; i++) { NamedPic *n = CArrayGet(pics, i); CFREE(n->name); PicFree(&n->pic); } CArrayClear(pics); for (int i = 0; i < (int)sprites->size; i++) { NamedSpritesFree(CArrayGet(sprites, i)); } CArrayClear(sprites); }
void HealthPickupsUpdate(HealthPickups *h, int ticks) { // Don't spawn pickups if not allowed if (!AreHealthPickupsAllowed(gCampaign.Entry.Mode) || !gConfig.Game.HealthPickups) { return; } double scalar = 1.0; // Update time until next spawn based on: // Damage taken (find player with lowest health) int minHealth = ModeMaxHealth(gCampaign.Entry.Mode); int maxHealth = minHealth; for (int i = 0; i < (int)gPlayerDatas.size; i++) { const PlayerData *p = CArrayGet(&gPlayerDatas, i); if (!IsPlayerAlive(p)) { continue; } const TActor *player = CArrayGet(&gActors, p->Id); minHealth = MIN(minHealth, player->health); } // Double spawn rate if near 0 health scalar *= (minHealth + maxHealth) / (maxHealth * 2.0); // Scale down over time scalar *= pow(TIME_DECAY_EXPONENT, h->pickupsSpawned); h->timeUntilNextSpawn = (int)floor(scalar * SPAWN_TIME); // Update time h->timer += ticks; // Attempt to add health if time reached, and we haven't placed too many if (h->timer >= h->timeUntilNextSpawn && h->map->NumExplorableTiles / MAX_TILES_PER_PICKUP + 1 > h->numPickups) { h->timer = 0; if (TryPlacePickup(h)) { h->pickupsSpawned++; h->numPickups++; } } }
static void LoadGunSpawners(CArray *classes, const CArray *guns) { for (int i = 0; i < (int)guns->size; i++) { const GunDescription *g = CArrayGet(guns, i); if (!g->IsRealGun) { continue; } MapObject m; memset(&m, 0, sizeof m); char buf[256]; sprintf(buf, "%s spawner", g->name); CSTRDUP(m.Name, buf); m.Normal.Pic = PicManagerGetPic(&gPicManager, "spawn_pad"); m.Normal.Offset = Vec2iNew( -m.Normal.Pic->size.x / 2, TILE_HEIGHT / 2 - m.Normal.Pic->size.y); m.Size = Vec2iNew(TILE_WIDTH, TILE_HEIGHT); m.Health = 0; m.Type = MAP_OBJECT_TYPE_PICKUP_SPAWNER; sprintf(buf, "gun_%s", g->name); m.u.PickupClass = StrPickupClass(buf); CArrayPushBack(classes, &m); } }
static void MissionBriefingDraw(void *data) { const MissionBriefingData *mData = data; GraphicsBlitBkg(&gGraphicsDevice); // Mission title FontStrOpt(mData->Title, Vec2iZero(), mData->TitleOpts); // Display password FontStrOpt(mData->Password, Vec2iZero(), mData->PasswordOpts); // Display description with typewriter effect FontStr(mData->TypewriterBuf, mData->DescriptionPos); // Display objectives for (int i = 0; i < (int)mData->MissionOptions->missionData->Objectives.size; i++) { const MissionObjective *o = CArrayGet(&mData->MissionOptions->missionData->Objectives, i); // Do not brief optional objectives if (o->Required == 0) { continue; } const Vec2i yInc = Vec2iNew(0, i * mData->ObjectiveHeight); FontStr(o->Description, Vec2iAdd(mData->ObjectiveDescPos, yInc)); DrawObjectiveInfo( mData->MissionOptions, i, Vec2iAdd(mData->ObjectiveInfoPos, yInc)); } }
static TActor *AIGetClosestActor(Vec2i from, int (*compFunc)(TActor *)) { // Search all the actors and find the closest one that // satisfies the condition TActor *closest = NULL; int minDistance = -1; for (int i = 0; i < (int)gActors.size; i++) { TActor *a = CArrayGet(&gActors, i); if (!a->isInUse || a->dead) { continue; } // Never target invulnerables or civilians if (a->flags & (FLAGS_INVULNERABLE | FLAGS_PENALTY)) { continue; } if (compFunc(a)) { int distance = CHEBYSHEV_DISTANCE(from.x, from.y, a->Pos.x, a->Pos.y); if (!closest || distance < minDistance) { minDistance = distance; closest = a; } } } return closest; }
static json_t *SaveStaticTiles(Mission *m) { // Create a text buffer for CSV // The buffer will contain n*5 chars (tiles, allow 5 chars each), // and n - 1 commas, so 6n total const int size = (int)m->u.Static.Tiles.size; if (size == 0) { return json_new_string(""); } char *bigbuf; CCALLOC(bigbuf, size * 6); char *pBuf = bigbuf; CASSERT(pBuf != NULL, "memory error"); for (int i = 0; i < size; i++) { char buf[32]; sprintf(buf, "%d", *(unsigned short *)CArrayGet( &m->u.Static.Tiles, i)); strcpy(pBuf, buf); pBuf += strlen(buf); if (i < size - 1) { *pBuf = ','; pBuf++; } } json_t *node = json_new_string(bigbuf); CFREE(bigbuf); return node; }
TObject *AIGetObjectRunningInto(TActor *a, int cmd) { // Check the position just in front of the character; // check if there's a (non-dangerous) object in front of it Vec2i frontPos = Vec2iFull2Real(a->Pos); TTileItem *item; if (cmd & CMD_LEFT) { frontPos.x--; } else if (cmd & CMD_RIGHT) { frontPos.x++; } if (cmd & CMD_UP) { frontPos.y--; } else if (cmd & CMD_DOWN) { frontPos.y++; } item = GetItemOnTileInCollision( &a->tileItem, frontPos, TILEITEM_IMPASSABLE, CalcCollisionTeam(1, a), gCampaign.Entry.Mode == CAMPAIGN_MODE_DOGFIGHT); if (!item || item->kind != KIND_OBJECT) { return NULL; } return CArrayGet(&gObjs, item->id); }
TBadGuy ConvertTBadGuy(Character *e) { TBadGuy b; b.armedBodyPic = BODY_ARMED; b.unarmedBodyPic = BODY_UNARMED; b.facePic = e->looks.face; b.speed = e->speed; b.probabilityToMove = e->bot->probabilityToMove; b.probabilityToTrack = e->bot->probabilityToTrack; b.probabilityToShoot = e->bot->probabilityToShoot; b.actionDelay = e->bot->actionDelay; for (int i = 0; i < (int)gGunDescriptions.size; i++) { const GunDescription *g = CArrayGet(&gGunDescriptions, i); if (strcmp(e->Gun->name, g->name) == 0) { b.gun = i; break; } } b.skinColor = e->looks.skin; b.armColor = e->looks.arm; b.bodyColor = e->looks.body; b.legColor = e->looks.leg; b.hairColor = e->looks.hair; b.health = e->maxHealth; b.flags = e->flags; return b; }
void MenuDisplay(const MenuSystem *ms) { const menu_t *menu = ms->current; if (menu->type == MENU_TYPE_CUSTOM) { menu->u.customData.displayFunc( menu, ms->graphics, ms->pos, ms->size, menu->u.customData.data); } else { MenuDisplayItems(ms); if (strlen(menu->u.normal.title) != 0) { FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.Area = ms->size; opts.Pad = Vec2iNew(20, 20); FontStrOpt(menu->u.normal.title, ms->pos, opts); } MenuDisplaySubmenus(ms); } for (int i = 0; i < (int)ms->customDisplayFuncs.size; i++) { MenuCustomDisplayFunc *cdf = CArrayGet(&ms->customDisplayFuncs, i); cdf->Func(NULL, ms->graphics, ms->pos, ms->size, cdf->Data); } if (menu->customDisplayFunc) { menu->customDisplayFunc( menu, ms->graphics, ms->pos, ms->size, menu->customDisplayData); } }
static char *MakeMapObjectTooltip(const MapObject *mo) { // Add a descriptive tooltip for the map object char buf[512]; // Construct text representing explosion guns char exBuf[256]; strcpy(exBuf, ""); if (mo->DestroyGuns.size > 0) { sprintf(exBuf, "\nExplodes: "); for (int i = 0; i < (int)mo->DestroyGuns.size; i++) { if (i > 0) { strcat(exBuf, ", "); } const GunDescription **g = CArrayGet(&mo->DestroyGuns, i); strcat(exBuf, (*g)->name); } } sprintf(buf, "%s\nHealth: %d%s", mo->Name, mo->Health, exBuf); char *tmp; CSTRDUP(tmp, buf); return tmp; }
void CollideAllItems( const TTileItem *item, const Vec2i pos, const int mask, const CollisionTeam team, const bool isPVP, CollideItemFunc func, void *data) { const Vec2i tv = Vec2iToTile(pos); Vec2i dv; // Check collisions with all other items on this tile, in all 8 directions for (dv.y = -1; dv.y <= 1; dv.y++) { for (dv.x = -1; dv.x <= 1; dv.x++) { const Vec2i dtv = Vec2iAdd(tv, dv); if (!MapIsTileIn(&gMap, dtv)) { continue; } CArray *tileThings = &MapGetTile(&gMap, dtv)->things; for (int i = 0; i < (int)tileThings->size; i++) { TTileItem *ti = ThingIdGetTileItem(CArrayGet(tileThings, i)); // Don't collide if items are on the same team if (!CollisionIsOnSameTeam(ti, team, isPVP)) { if (item != ti && (ti->flags & mask) && ItemsCollide(item, ti, pos)) { func(ti, data); } } } } } }
TTileItem *GetItemOnTileInCollision( TTileItem *item, Vec2i pos, int mask, CollisionTeam team, int isDogfight) { Vec2i tv = Vec2iToTile(pos); Vec2i dv; if (!MapIsTileIn(&gMap, tv)) { return NULL; } // Check collisions with all other items on this tile, in all 8 directions for (dv.y = -1; dv.y <= 1; dv.y++) { for (dv.x = -1; dv.x <= 1; dv.x++) { CArray *tileThings = &MapGetTile(&gMap, Vec2iAdd(tv, dv))->things; for (int i = 0; i < (int)tileThings->size; i++) { TTileItem *ti = ThingIdGetTileItem(CArrayGet(tileThings, i)); // Don't collide if items are on the same team if (!CollisionIsOnSameTeam(ti, team, isDogfight)) { if (item != ti && (ti->flags & mask) && ItemsCollide(item, ti, pos)) { return ti; } } } } } return NULL; }