static const MapObject *LoadMapObjectRef(json_t *itemNode, const int version) { if (version <= 3) { int idx; LoadInt(&idx, itemNode, "Index"); return IntMapObject(idx); } else { const char *moName = json_find_first_label(itemNode, "MapObject")->child->text; const MapObject *mo = StrMapObject(moName); if (mo == NULL && version <= 11 && StrEndsWith(moName, " spawner")) { char buf[256]; // Old version had same name for ammo and gun spawner char itemName[256]; strncpy(itemName, moName, strlen(moName) - strlen(" spawner")); itemName[strlen(moName) - strlen(" spawner")] = '\0'; snprintf(buf, 256, "%s ammo spawner", itemName); mo = StrMapObject(buf); } if (mo == NULL) { LOG(LM_MAP, LL_ERROR, "Failed to load map object (%s)", moName); } return mo; } }
void MapObjectsLoadJSON(CArray *classes, json_t *root) { int version; LoadInt(&version, root, "Version"); if (version > VERSION || version <= 0) { CASSERT(false, "cannot read map objects file version"); return; } json_t *pickupsNode = json_find_first_label(root, "MapObjects")->child; for (json_t *child = pickupsNode->child; child; child = child->next) { MapObject m; LoadMapObject(&m, child); CArrayPushBack(classes, &m); } ReloadDestructibles(&gMapObjects); // Load blood objects CArrayClear(&gMapObjects.Bloods); for (int i = 0;; i++) { char buf[CDOGS_FILENAME_MAX]; sprintf(buf, "blood%d", i); if (StrMapObject(buf) == NULL) { break; } char *tmp; CSTRDUP(tmp, buf); CArrayPushBack(&gMapObjects.Bloods, &tmp); } }
static void DrawWreck( UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData) { const IndexedEditorBrush *data = vData; const char **name = CArrayGet(&gMapObjects.Destructibles, data->u.ItemIndex); const MapObject *mo = StrMapObject(*name); pos = Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2)); Vec2i offset; const Pic *pic = MapObjectGetPic(mo, &offset, true); Blit(g, pic, Vec2iAdd(pos, offset)); }
MapObject *IntMapObject(const int m) { // Note: do not edit; legacy integer mapping static const char *oldMapObjects[] = { "barrel_blue", "box", "box2", "cabinet", "plant", "bench", "chair", "column", "barrel_skull", "barrel_wood", "box_gray", "box_green", "statue_ogre", "table_wood_candle", "table_wood", "tree_dead", "bookshelf", "box_wood", "table_clothed", "table_steel", "tree_autumn", "tree", "box_metal_green", "safe", "box_red", "table_lab", "terminal", "barrel", "rocket", "egg", "bloodstain", "wall_skull", "bone_blood", "bulletmarks", "skull", "blood0", "scratch", "wall_stuff", "wall_goo", "goo" }; if (m < 0 || m > (int)(sizeof oldMapObjects / sizeof oldMapObjects[0])) { LOG(LM_MAIN, LL_ERROR, "cannot find map object %d", m); return NULL; } return StrMapObject(oldMapObjects[m]); }
static const MapObject *LoadMapObjectRef(json_t *itemNode, const int version) { if (version <= 3) { int idx; LoadInt(&idx, itemNode, "Index"); return IntMapObject(idx); } else { return StrMapObject( json_find_first_label(itemNode, "MapObject")->child->text); } }
void ObjectiveLoadJSON(Objective *o, json_t *node, const int version) { memset(o, 0, sizeof *o); o->Description = GetString(node, "Description"); JSON_UTILS_LOAD_ENUM(o->Type, node, "Type", StrObjectiveType); // Set objective colours based on type o->color = ObjectiveTypeColor(o->Type); if (version < 8) { // Index numbers used for all objective classes; convert them // to their class handles LoadInt(&o->u.Index, node, "Index"); switch (o->Type) { case OBJECTIVE_COLLECT: o->u.Pickup = IntPickupClass(o->u.Index); break; case OBJECTIVE_DESTROY: o->u.MapObject = IntMapObject(o->u.Index); break; default: // do nothing break; } } else { char *tmp; switch (o->Type) { case OBJECTIVE_COLLECT: tmp = GetString(node, "Pickup"); o->u.Pickup = StrPickupClass(tmp); CFREE(tmp); break; case OBJECTIVE_DESTROY: tmp = GetString(node, "MapObject"); o->u.MapObject = StrMapObject(tmp); CFREE(tmp); break; default: LoadInt(&o->u.Index, node, "Index"); break; } } LoadInt(&o->Count, node, "Count"); LoadInt(&o->Required, node, "Required"); LoadInt(&o->Flags, node, "Flags"); }
int DestructibleMapObjectIndex(const MapObject *mo) { if (mo == NULL) { return 0; } CA_FOREACH(const char *, name, gMapObjects.Destructibles) const MapObject *d = StrMapObject(*name); if (d == mo) { return _ca_index; } CA_FOREACH_END() CASSERT(false, "cannot find destructible map object"); return -1; }
static void MissionResetObjectiveIndex(Objective *o) { switch (o->Type) { case OBJECTIVE_COLLECT: o->u.Pickup = IntScorePickupClass(0); break; case OBJECTIVE_DESTROY: { const char **destructibleName = CArrayGet(&gMapObjects.Destructibles, 0); o->u.MapObject = StrMapObject(*destructibleName); } CASSERT(o->u.MapObject != NULL, "cannot find map object"); break; default: o->u.Index = 0; break; } }
static UIObject *CreateAddWreckObjs(Vec2i pos, EditorBrush *brush) { UIObject *o2; UIObject *c = UIObjectCreate(UITYPE_CONTEXT_MENU, 0, pos, Vec2iZero()); UIObject *o = UIObjectCreate( UITYPE_CUSTOM, 0, Vec2iZero(), Vec2iNew(TILE_WIDTH + 4, TILE_HEIGHT + 4)); o->ChangeFunc = BrushSetBrushTypeAddWreck; o->u.CustomDrawFunc = DrawWreck; o->OnFocusFunc = ActivateIndexedEditorBrush; o->OnUnfocusFunc = DeactivateIndexedEditorBrush; pos = Vec2iZero(); const int width = 8; for (int i = 0; i < (int)gMapObjects.Destructibles.size; i++) { o2 = UIObjectCopy(o); o2->IsDynamicData = 1; CMALLOC(o2->Data, sizeof(IndexedEditorBrush)); ((IndexedEditorBrush *)o2->Data)->Brush = brush; ((IndexedEditorBrush *)o2->Data)->u.ItemIndex = i; o2->Pos = pos; UIObjectAddChild(c, o2); pos.x += o->Size.x; if (((i + 1) % width) == 0) { pos.x = 0; pos.y += o->Size.y; } const char **name = CArrayGet(&gMapObjects.Destructibles, i); const MapObject *mo = StrMapObject(*name); CSTRDUP(o2->Tooltip, mo->Name); } UIObjectDestroy(o); return c; }
void ObjAdd(const NMapObjectAdd amo) { // Find an empty slot in object list TObject *o = NULL; int i; for (i = 0; i < (int)gObjs.size; i++) { TObject *obj = CArrayGet(&gObjs, i); if (!obj->isInUse) { o = obj; break; } } if (o == NULL) { TObject obj; memset(&obj, 0, sizeof obj); CArrayPushBack(&gObjs, &obj); i = (int)gObjs.size - 1; o = CArrayGet(&gObjs, i); } memset(o, 0, sizeof *o); o->uid = amo.UID; o->Class = StrMapObject(amo.MapObjectClass); o->Health = amo.Health; o->tileItem.x = o->tileItem.y = -1; o->tileItem.flags = amo.TileItemFlags; o->tileItem.kind = KIND_OBJECT; o->tileItem.getPicFunc = GetObjectPic; o->tileItem.getActorPicsFunc = NULL; o->tileItem.size = o->Class->Size; o->tileItem.id = i; MapTryMoveTileItem(&gMap, &o->tileItem, Net2Vec2i(amo.Pos)); o->isInUse = true; }
MapObject *RandomBloodMapObject(const MapObjects *mo) { const int idx = rand() % (int)mo->Bloods.size; const char **name = CArrayGet(&mo->Bloods, idx); return StrMapObject(*name); }
EditorResult EditorBrushStartPainting(EditorBrush *b, Mission *m, int isMain) { if (!b->IsPainting) { b->LastPos = b->Pos; } b->PaintType = isMain ? b->MainType : b->SecondaryType; switch (b->Type) { case BRUSHTYPE_POINT: b->IsPainting = true; EditorBrushPaintLine(b, m); return EDITOR_RESULT_CHANGED; case BRUSHTYPE_LINE: // fallthrough case BRUSHTYPE_BOX: // fallthrough case BRUSHTYPE_BOX_FILLED: // fallthrough case BRUSHTYPE_ROOM: // fallthrough case BRUSHTYPE_SET_EXIT: // don't paint until the end break; case BRUSHTYPE_ROOM_PAINTER: EditorBrushPaintRoom(b, m); return EDITOR_RESULT_CHANGED; case BRUSHTYPE_SELECT: // Perform state changes if we've started painting if (!b->IsPainting) { if (!b->IsMoving) { // check if the click was inside the selection // If so, start moving if (b->Pos.x >= b->SelectionStart.x && b->Pos.y >= b->SelectionStart.y && b->Pos.x < b->SelectionStart.x + b->SelectionSize.x && b->Pos.y < b->SelectionStart.y + b->SelectionSize.y) { b->IsMoving = 1; b->DragPos = b->Pos; } } } break; case BRUSHTYPE_FILL: // Use flood fill to change all the tiles of the same type to // another type // Don't paint if target already same type // Special case: don't flood-fill doors if ((MissionGetTile(m, b->Pos) & MAP_MASKACCESS) != b->PaintType && b->PaintType != MAP_DOOR) { FloodFillData data; data.Fill = MissionFillTile; data.IsSame = MissionIsTileSame; PaintFloodFillData pData; pData.m = m; pData.fromType = MissionGetTile(m, b->Pos) & MAP_MASKACCESS; pData.toType = b->PaintType; data.data = &pData; if (CFloodFill(b->Pos, &data)) { return EDITOR_RESULT_CHANGED; } } return EDITOR_RESULT_NONE; case BRUSHTYPE_SET_PLAYER_START: if (MissionGetTile(m, b->Pos) == MAP_ROOM || MissionGetTile(m, b->Pos) == MAP_FLOOR) { m->u.Static.Start = b->Pos; return EDITOR_RESULT_CHANGED; } break; case BRUSHTYPE_ADD_ITEM: if (isMain) { if (MissionStaticTryAddItem(m, b->u.MapObject, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } else { if (MissionStaticTryRemoveItemAt(m, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } break; case BRUSHTYPE_ADD_WRECK: if (isMain) { const char **destructibleName = CArrayGet(&gMapObjects.Destructibles, b->u.ItemIndex); if (MissionStaticTryAddWreck( m, StrMapObject(*destructibleName), b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } else { if (MissionStaticTryRemoveWreckAt(m, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } break; case BRUSHTYPE_ADD_CHARACTER: if (isMain) { if (MissionStaticTryAddCharacter(m, b->u.ItemIndex, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } else { if (MissionStaticTryRemoveCharacterAt(m, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } break; case BRUSHTYPE_ADD_OBJECTIVE: if (isMain) { if (MissionStaticTryAddObjective( m, b->u.ItemIndex, b->Index2, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } else { if (MissionStaticTryRemoveObjectiveAt(m, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } break; case BRUSHTYPE_ADD_KEY: if (isMain) { if (MissionStaticTryAddKey(m, b->u.ItemIndex, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } else { if (MissionStaticTryRemoveKeyAt(m, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } break; case BRUSHTYPE_SET_KEY: if (isMain || b->u.ItemIndex > 0) { if (MissionStaticTrySetKey(m, b->u.ItemIndex, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } else { if (MissionStaticTryUnsetKeyAt(m, b->Pos)) { return EDITOR_RESULT_CHANGED_AND_RELOAD; } } break; default: assert(0 && "unknown brush type"); break; } b->IsPainting = 1; return EDITOR_RESULT_NONE; }
void LoadMissions(CArray *missions, json_t *missionsNode, int version) { json_t *child; for (child = missionsNode->child; child; child = child->next) { Mission m; MissionInit(&m); m.Title = GetString(child, "Title"); m.Description = GetString(child, "Description"); JSON_UTILS_LOAD_ENUM(m.Type, child, "Type", StrMapType); LoadInt(&m.Size.x, child, "Width"); LoadInt(&m.Size.y, child, "Height"); if (version <= 10) { int style; LoadInt(&style, child, "WallStyle"); strcpy(m.WallStyle, IntWallStyle(style)); LoadInt(&style, child, "FloorStyle"); strcpy(m.FloorStyle, IntFloorStyle(style)); LoadInt(&style, child, "RoomStyle"); strcpy(m.RoomStyle, IntRoomStyle(style)); } else { char *tmp = GetString(child, "WallStyle"); strcpy(m.WallStyle, tmp); CFREE(tmp); tmp = GetString(child, "FloorStyle"); strcpy(m.FloorStyle, tmp); CFREE(tmp); tmp = GetString(child, "RoomStyle"); strcpy(m.RoomStyle, tmp); CFREE(tmp); } if (version <= 9) { int style; LoadInt(&style, child, "ExitStyle"); strcpy(m.ExitStyle, IntExitStyle(style)); } else { char *tmp = GetString(child, "ExitStyle"); strcpy(m.ExitStyle, tmp); CFREE(tmp); } if (version <= 8) { int keyStyle; LoadInt(&keyStyle, child, "KeyStyle"); strcpy(m.KeyStyle, IntKeyStyle(keyStyle)); } else { char *tmp = GetString(child, "KeyStyle"); strcpy(m.KeyStyle, tmp); CFREE(tmp); } if (version <= 5) { int doorStyle; LoadInt(&doorStyle, child, "DoorStyle"); strcpy(m.DoorStyle, IntDoorStyle(doorStyle)); } else { char *tmp = GetString(child, "DoorStyle"); strcpy(m.DoorStyle, tmp); CFREE(tmp); } LoadMissionObjectives( &m.Objectives, json_find_first_label(child, "Objectives")->child, version); LoadIntArray(&m.Enemies, child, "Enemies"); LoadIntArray(&m.SpecialChars, child, "SpecialChars"); if (version <= 3) { CArray items; CArrayInit(&items, sizeof(int)); LoadIntArray(&items, child, "Items"); CArray densities; CArrayInit(&densities, sizeof(int)); LoadIntArray(&densities, child, "ItemDensities"); for (int i = 0; i < (int)items.size; i++) { MapObjectDensity mod; mod.M = IntMapObject(*(int *)CArrayGet(&items, i)); mod.Density = *(int *)CArrayGet(&densities, i); CArrayPushBack(&m.MapObjectDensities, &mod); } } else { json_t *modsNode = json_find_first_label(child, "MapObjectDensities"); if (modsNode && modsNode->child) { modsNode = modsNode->child; for (json_t *modNode = modsNode->child; modNode; modNode = modNode->next) { MapObjectDensity mod; mod.M = StrMapObject( json_find_first_label(modNode, "MapObject")->child->text); LoadInt(&mod.Density, modNode, "Density"); CArrayPushBack(&m.MapObjectDensities, &mod); } } } LoadInt(&m.EnemyDensity, child, "EnemyDensity"); LoadWeapons( &m.Weapons, json_find_first_label(child, "Weapons")->child); strcpy(m.Song, json_find_first_label(child, "Song")->child->text); if (version <= 4) { // Load colour indices int wc, fc, rc, ac; LoadInt(&wc, child, "WallColor"); LoadInt(&fc, child, "FloorColor"); LoadInt(&rc, child, "RoomColor"); LoadInt(&ac, child, "AltColor"); m.WallMask = RangeToColor(wc); m.FloorMask = RangeToColor(fc); m.RoomMask = RangeToColor(rc); m.AltMask = RangeToColor(ac); } else { LoadColor(&m.WallMask, child, "WallMask"); LoadColor(&m.FloorMask, child, "FloorMask"); LoadColor(&m.RoomMask, child, "RoomMask"); LoadColor(&m.AltMask, child, "AltMask"); } switch (m.Type) { case MAPTYPE_CLASSIC: LoadInt(&m.u.Classic.Walls, child, "Walls"); LoadInt(&m.u.Classic.WallLength, child, "WallLength"); LoadInt(&m.u.Classic.CorridorWidth, child, "CorridorWidth"); LoadClassicRooms( &m, json_find_first_label(child, "Rooms")->child); LoadInt(&m.u.Classic.Squares, child, "Squares"); LoadClassicDoors(&m, child, "Doors"); LoadClassicPillars(&m, child, "Pillars"); break; case MAPTYPE_STATIC: if (!TryLoadStaticMap(&m, child, version)) { continue; } break; case MAPTYPE_CAVE: LoadInt(&m.u.Cave.FillPercent, child, "FillPercent"); LoadInt(&m.u.Cave.Repeat, child, "Repeat"); LoadInt(&m.u.Cave.R1, child, "R1"); LoadInt(&m.u.Cave.R2, child, "R2"); break; default: assert(0 && "unknown map type"); continue; } CArrayPushBack(missions, &m); } }