diff --git a/src/cdogs/actors.c b/src/cdogs/actors.c index 61d95c1aa..9da0aa0f9 100644 --- a/src/cdogs/actors.c +++ b/src/cdogs/actors.c @@ -1001,9 +1001,8 @@ static bool ActorTryChangeDirection( { const bool willChangeDirecton = !actor->petrified && CMD_HAS_DIRECTION(cmd) && - (!Button2(cmd) || - ConfigGetEnum(&gConfig, "Game.SwitchMoveStyle") != - SWITCHMOVE_STRAFE) && + (!Button2(cmd) || ConfigGetEnum(&gConfig, "Game.SwitchMoveStyle") != + SWITCHMOVE_STRAFE) && (!Button1(prevCmd) || ConfigGetEnum(&gConfig, "Game.FireMoveStyle") != FIREMOVE_STRAFE); const direction_e dir = CmdToDirection(cmd); @@ -1128,13 +1127,13 @@ void CommandActor(TActor *actor, int cmd, int ticks) static bool ActorTryMove(TActor *actor, int cmd, int ticks) { const bool canMoveWhenShooting = - actor->PlayerUID < 0 ? (actor->flags & FLAGS_MOVE_AND_SHOOT) : - ( - ConfigGetEnum(&gConfig, "Game.FireMoveStyle") != FIREMOVE_STOP || - (ConfigGetEnum(&gConfig, "Game.SwitchMoveStyle") == - SWITCHMOVE_STRAFE && - Button2(cmd)) - ); + actor->PlayerUID < 0 + ? (actor->flags & FLAGS_MOVE_AND_SHOOT) + : (ConfigGetEnum(&gConfig, "Game.FireMoveStyle") != + FIREMOVE_STOP || + (ConfigGetEnum(&gConfig, "Game.SwitchMoveStyle") == + SWITCHMOVE_STRAFE && + Button2(cmd))); const bool canMove = !actor->hasShot || canMoveWhenShooting; const bool willMove = !actor->petrified && CMD_HAS_DIRECTION(cmd) && canMove; @@ -1479,7 +1478,7 @@ static void ActorDie(TActor *actor) GameEvent e = GameEventNew(GAME_EVENT_ACTOR_DIE); e.u.ActorDie.UID = actor->uid; GameEventsEnqueue(&gGameEvents, e); - + // Persist player ammo for when they respawn ActorPersistPlayerWeaponsAndAmmo(actor); } @@ -1701,10 +1700,10 @@ TActor *ActorAdd(NActorAdd aa) { // Use persisted ammo amount if it is greater, // or if buy/sell is enabled - if (gCampaign.Setting.BuyAndSell || - (int)aa.Ammo[i].Amount > - AmmoGetById(&gAmmo, aa.Ammo[i].Id)->Amount * - AMMO_STARTING_MULTIPLE) + const Ammo *ammo = AmmoGetById(&gAmmo, aa.Ammo[i].Id); + if (ammo && (gCampaign.Setting.BuyAndSell || + (int)aa.Ammo[i].Amount > + ammo->Amount * AMMO_STARTING_MULTIPLE)) { CArraySet(&actor->ammo, aa.Ammo[i].Id, &aa.Ammo[i].Amount); } diff --git a/src/cdogs/ammo.c b/src/cdogs/ammo.c index b3859e3b4..d71ff9b5f 100644 --- a/src/cdogs/ammo.c +++ b/src/cdogs/ammo.c @@ -1,29 +1,29 @@ /* - C-Dogs SDL - A port of the legendary (and fun) action/arcade cdogs. - Copyright (c) 2014, 2016-2019, 2023 Cong Xu - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. + C-Dogs SDL + A port of the legendary (and fun) action/arcade cdogs. + Copyright (c) 2014, 2016-2019, 2023-2024 Cong Xu + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ #include "ammo.h" @@ -34,10 +34,8 @@ #include "pic_manager.h" #include "utils.h" - AmmoClasses gAmmo; - // TODO: use map structure? Ammo *StrAmmo(const char *s) { @@ -50,16 +48,16 @@ int StrAmmoId(const char *s) return 0; } CA_FOREACH(Ammo, a, gAmmo.CustomAmmo) - if (strcmp(s, a->Name) == 0) - { - return _ca_index + (int)gAmmo.Ammo.size; - } + if (strcmp(s, a->Name) == 0) + { + return _ca_index + (int)gAmmo.Ammo.size; + } CA_FOREACH_END() CA_FOREACH(Ammo, a, gAmmo.Ammo) - if (strcmp(s, a->Name) == 0) - { - return _ca_index; - } + if (strcmp(s, a->Name) == 0) + { + return _ca_index; + } CA_FOREACH_END() CASSERT(false, "cannot parse ammo name"); return 0; @@ -87,8 +85,8 @@ void AmmoInitialize(AmmoClasses *ammo, const char *path) e = json_stream_parse(f, &root); if (e != JSON_OK) { - LOG(LM_MAIN, LL_ERROR, "Error parsing ammo file %s [error %d]", - buf, (int)e); + LOG(LM_MAIN, LL_ERROR, "Error parsing ammo file %s [error %d]", buf, + (int)e); goto bail; } AmmoLoadJSON(&ammo->Ammo, root); @@ -139,9 +137,9 @@ static void LoadAmmo(Ammo *a, json_t *node, const int version) void AmmoClassesClear(CArray *ammo) { CA_FOREACH(Ammo, a, *ammo) - CFREE(a->Name); - CFREE(a->Sound); - CFREE(a->DefaultGun); + CFREE(a->Name); + CFREE(a->Sound); + CFREE(a->DefaultGun); CA_FOREACH_END() CArrayClear(ammo); } @@ -159,10 +157,12 @@ Ammo *AmmoGetById(AmmoClasses *ammo, const int id) { return CArrayGet(&ammo->Ammo, id); } - else + const int id2 = id - (int)ammo->Ammo.size; + if (id < (int)ammo->CustomAmmo.size) { - return CArrayGet(&ammo->CustomAmmo, id - (int)ammo->Ammo.size); + return CArrayGet(&ammo->CustomAmmo, id2); } + return NULL; } int AmmoGetNumClasses(const AmmoClasses *ammo) @@ -170,7 +170,6 @@ int AmmoGetNumClasses(const AmmoClasses *ammo) return (int)ammo->Ammo.size + (int)ammo->CustomAmmo.size; } - bool AmmoIsLow(const Ammo *a, const int amount) { // Low ammo if less than or equal to half the default amount diff --git a/src/cdogs/map_build.c b/src/cdogs/map_build.c index d32ac2884..e32fe0f15 100644 --- a/src/cdogs/map_build.c +++ b/src/cdogs/map_build.c @@ -22,7 +22,7 @@ This file incorporates work covered by the following copyright and permission notice: - Copyright (c) 2013-2014, 2017-2023 Cong Xu + Copyright (c) 2013-2014, 2017-2024 Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without @@ -69,7 +69,9 @@ static void MapSetupDoors(MapBuilder *mb); static void MapAddDrains(MapBuilder *mb); static void MapGenerateRandomExitArea(Map *map, const int mission); void MapBuild( - Map *m, const Mission *mission, const bool loadDynamic, const int missionIndex, const GameMode mode, const CharacterStore *characters) + Map *m, const Mission *mission, const bool loadDynamic, + const int missionIndex, const GameMode mode, + const CharacterStore *characters) { MapBuilder mb; MapBuilderInit(&mb, m, mission, mode, characters); @@ -188,14 +190,16 @@ static void MapSetupDoors(MapBuilder *mb) { // Mark doors as set up as we go CArray doorsSetup; - CArrayInitFillZero(&doorsSetup, sizeof(bool), mb->Map->Size.x * mb->Map->Size.y); - + CArrayInitFillZero( + &doorsSetup, sizeof(bool), mb->Map->Size.x * mb->Map->Size.y); + RECT_FOREACH(Rect2iNew(svec2i_zero(), mb->Map->Size)) // Check if this door tile hasn't been set up yet const int idx = _v.x + _v.y * mb->Map->Size.x; if (MapBuilderIsDoor(mb, _v) && !*(bool *)CArrayGet(&doorsSetup, idx)) { - const struct vec2i groupSize = MapAddDoorGroup(mb, _v, MapGetAccessFlags(mb, _v)); + const struct vec2i groupSize = + MapAddDoorGroup(mb, _v, MapGetAccessFlags(mb, _v)); for (int dx = 0; dx < groupSize.x; dx++) { for (int dy = 0; dy < groupSize.y; dy++) @@ -206,7 +210,7 @@ static void MapSetupDoors(MapBuilder *mb) } } RECT_FOREACH_END() - + CArrayTerminate(&doorsSetup); } static bool MapBuilderIsDoor(const MapBuilder *mb, const struct vec2i v) @@ -230,7 +234,8 @@ static int MapGetAccessFlags(const MapBuilder *mb, const struct vec2i v) } void MapBuilderInit( - MapBuilder *mb, Map *m, const Mission *mission, const GameMode mode, const CharacterStore *characters) + MapBuilder *mb, Map *m, const Mission *mission, const GameMode mode, + const CharacterStore *characters) { memset(mb, 0, sizeof *mb); mb->Map = m; @@ -440,7 +445,7 @@ void MapLoadDynamic(MapBuilder *mb) MapTryPlaceOneObject(mb, MapGetRandomTile(mb->Map), mod->M, 0, true); } CA_FOREACH_END() - + CA_FOREACH(const PickupCount, pc, mb->mission->PickupCounts) for (int j = 0; j < pc->Count; j++) { @@ -467,7 +472,8 @@ void MapLoadDynamic(MapBuilder *mb) AddKeys(mb); } } -static bool MapTryPlaceBlowup(MapBuilder *mb, const int objective, const bool strict); +static bool MapTryPlaceBlowup( + MapBuilder *mb, const int objective, const bool strict); static int MapTryPlaceCollectible(MapBuilder *mb, const int objective); static void AddObjectives(MapBuilder *mb) { @@ -583,7 +589,8 @@ typedef struct } TryPlaceOneBlowupData; static bool TryPlaceOneBlowup( MapBuilder *mb, const struct vec2i tilePos, void *data); -static bool MapTryPlaceBlowup(MapBuilder *mb, const int objective, const bool strict) +static bool MapTryPlaceBlowup( + MapBuilder *mb, const int objective, const bool strict) { TryPlaceOneBlowupData data; data.o = CArrayGet(&mb->mission->Objectives, objective); @@ -597,7 +604,8 @@ static bool TryPlaceOneBlowup( MapBuilder *mb, const struct vec2i tilePos, void *data) { const TryPlaceOneBlowupData *pData = data; - return MapTryPlaceDestroyObject(mb, mb->mission, pData->objective, tilePos, pData->strict); + return MapTryPlaceDestroyObject( + mb, mb->mission, pData->objective, tilePos, pData->strict); } void MapBuilderSetLeaveFree( @@ -657,7 +665,8 @@ static void MapSetupTilesAndWalls(MapBuilder *mb) MapSetupTile(mb, _v); RECT_FOREACH_END() - if (mb->mission->Type != MAPTYPE_STATIC || mb->mission->u.Static.AltFloorsEnabled) + if (mb->mission->Type != MAPTYPE_STATIC || + mb->mission->u.Static.AltFloorsEnabled) { // Randomly change normal floor tiles to alternative floor tiles for (int i = 0; i < mb->Map->Size.x * mb->Map->Size.y / 22; i++) @@ -667,9 +676,8 @@ static void MapSetupTilesAndWalls(MapBuilder *mb) { Tile *t = MapGetTile(mb->Map, pos); t->Class = TileClassesGetMaskedTile( - mb->Map->TileClasses, - t->Class, t->Class->Style, "alt1", t->Class->Mask, - t->Class->MaskAlt); + mb->Map->TileClasses, t->Class, t->Class->Style, "alt1", + t->Class->Mask, t->Class->MaskAlt); } } for (int i = 0; i < mb->Map->Size.x * mb->Map->Size.y / 16; i++) @@ -679,9 +687,8 @@ static void MapSetupTilesAndWalls(MapBuilder *mb) { Tile *t = MapGetTile(mb->Map, pos); t->Class = TileClassesGetMaskedTile( - mb->Map->TileClasses, - t->Class, t->Class->Style, "alt2", t->Class->Mask, - t->Class->MaskAlt); + mb->Map->TileClasses, t->Class, t->Class->Style, "alt2", + t->Class->Mask, t->Class->MaskAlt); } } } @@ -703,21 +710,20 @@ static void MapSetupTile(MapBuilder *mb, const struct vec2i pos) if (tc->Type == TILE_CLASS_FLOOR) { t->Class = TileClassesGetMaskedTile( - mb->Map->TileClasses, - tc, tc->Style, canSeeTileAbove ? "normal" : "shadow", tc->Mask, - tc->MaskAlt); + mb->Map->TileClasses, tc, tc->Style, + canSeeTileAbove ? "normal" : "shadow", tc->Mask, tc->MaskAlt); } else if (tc->Type == TILE_CLASS_WALL) { t->Class = TileClassesGetMaskedTile( - mb->Map->TileClasses, - tc, tc->Style, MapGetWallPic(mb, pos), tc->Mask, tc->MaskAlt); + mb->Map->TileClasses, tc, tc->Style, MapGetWallPic(mb, pos), + tc->Mask, tc->MaskAlt); } else if (tc->Type == TILE_CLASS_DOOR) { t->Class = TileClassesGetMaskedTile( - mb->Map->TileClasses, - tc, tc->Style, "normal_h", tc->Mask, tc->MaskAlt); + mb->Map->TileClasses, tc, tc->Style, "normal_h", tc->Mask, + tc->MaskAlt); } else if (tc->Type == TILE_CLASS_NOTHING) { @@ -899,12 +905,14 @@ static bool MapBuilderGetIsRoom(const MapBuilder *mb, const struct vec2i pos) } void MapMakeRoomWalls( - MapBuilder *mb, const RoomParams r, const TileClass *wall, const Rect2i room) + MapBuilder *mb, const RoomParams r, const TileClass *wall, + const Rect2i room) { int count = 0; for (int i = 0; i < 100 && count < r.Walls; i++) { - if (!MapTryBuildWall(mb, true, MAX(r.WallPad, 1), r.WallLength, wall, room)) + if (!MapTryBuildWall( + mb, true, MAX(r.WallPad, 1), r.WallLength, wall, room)) { continue; } @@ -920,9 +928,9 @@ bool MapTryBuildWall( const TileClass *wall, const Rect2i r) { const struct vec2i v = - Rect2iIsZero(r) ? - MapGetRandomTile(mb->Map) : - svec2i_add(r.Pos, svec2i(rand() % r.Size.x, rand() % r.Size.y)); + Rect2iIsZero(r) + ? MapGetRandomTile(mb->Map) + : svec2i_add(r.Pos, svec2i(rand() % r.Size.x, rand() % r.Size.y)); if (MapIsValidStartForWall(mb, v, isRoom, pad)) { MapBuilderSetTile(mb, v, wall); @@ -1090,11 +1098,14 @@ static void AddOverlapRooms( CA_FOREACH_END() } -static void PlaceDoors(MapBuilder *mb, const int doorSize, const int roomDim, const struct vec2i doorStart, const struct vec2i d, const bool randomPos, const TileClass *tile); +static void PlaceDoors( + MapBuilder *mb, const int doorSize, const int roomDim, + const struct vec2i doorStart, const struct vec2i d, const bool randomPos, + const TileClass *tile); void MapPlaceDoors( MapBuilder *mb, const Rect2i r, const bool hasDoors, const bool doors[4], - const int doorMin, const int doorMax, const uint16_t accessMask, const bool randomPos, - const TileClass *door, const TileClass *floor) + const int doorMin, const int doorMax, const uint16_t accessMask, + const bool randomPos, const TileClass *door, const TileClass *floor) { const TileClass *tile = hasDoors ? door : floor; @@ -1107,39 +1118,40 @@ void MapPlaceDoors( { continue; } - const int doorSize = doorMax > doorMin ? RAND_INT(doorMin, doorMax) : doorMin; + const int doorSize = + doorMax > doorMin ? RAND_INT(doorMin, doorMax) : doorMin; int roomDim; struct vec2i d; struct vec2i doorStart; switch (i) { - case 0: - // left - roomDim = r.Size.y; - d = svec2i(1, 0); - doorStart = r.Pos; - break; - case 1: - // right - roomDim = r.Size.y; - d = svec2i(1, 0); - doorStart = svec2i(r.Pos.x + r.Size.x - 1, r.Pos.y); - break; - case 2: - // top - roomDim = r.Size.x; - d = svec2i(0, 1); - doorStart = r.Pos; - break; - case 3: - // bottom: - roomDim = r.Size.x; - d = svec2i(0, 1); - doorStart = svec2i(r.Pos.x, r.Pos.y + r.Size.y - 1); - break; - default: - CASSERT(false, "unexpected side index"); - return; + case 0: + // left + roomDim = r.Size.y; + d = svec2i(1, 0); + doorStart = r.Pos; + break; + case 1: + // right + roomDim = r.Size.y; + d = svec2i(1, 0); + doorStart = svec2i(r.Pos.x + r.Size.x - 1, r.Pos.y); + break; + case 2: + // top + roomDim = r.Size.x; + d = svec2i(0, 1); + doorStart = r.Pos; + break; + case 3: + // bottom: + roomDim = r.Size.x; + d = svec2i(0, 1); + doorStart = svec2i(r.Pos.x, r.Pos.y + r.Size.y - 1); + break; + default: + CASSERT(false, "unexpected side index"); + return; } PlaceDoors(mb, doorSize, roomDim, doorStart, d, randomPos, tile); } @@ -1147,22 +1159,29 @@ void MapPlaceDoors( static bool TryPlaceDoorTile( MapBuilder *mb, const struct vec2i v, const struct vec2i d, const TileClass *tile); -static void PlaceDoors(MapBuilder *mb, const int doorSize, const int roomDim, const struct vec2i doorStart, const struct vec2i d, const bool randomPos, const TileClass *tile) +static void PlaceDoors( + MapBuilder *mb, const int doorSize, const int roomDim, + const struct vec2i doorStart, const struct vec2i d, const bool randomPos, + const TileClass *tile) { const struct vec2i dAcross = svec2i_subtract(svec2i_one(), d); const int size = MIN(doorSize, roomDim - 2); struct vec2i start = doorStart; if (randomPos) { - start = svec2i_add(start, svec2i_scale(dAcross, (float)RAND_INT(1, roomDim - size - 1))); + start = svec2i_add( + start, + svec2i_scale(dAcross, (float)RAND_INT(1, roomDim - size - 1))); } else { - start = svec2i_add(start, svec2i_scale(dAcross, (float)((roomDim - size) / 2))); + start = svec2i_add( + start, svec2i_scale(dAcross, (float)((roomDim - size) / 2))); } for (int i = 0; i < size; i++) { - const struct vec2i v = svec2i_add(start, svec2i_scale(dAcross, (float)i)); + const struct vec2i v = + svec2i_add(start, svec2i_scale(dAcross, (float)i)); if (!TryPlaceDoorTile(mb, v, d, tile)) { break; @@ -1437,7 +1456,9 @@ bool MapIsLessThanTwoWallOverlaps( return true; } -void MapFillRect(MapBuilder *mb, const Rect2i r, const TileClass *edge, const TileClass *fill) +void MapFillRect( + MapBuilder *mb, const Rect2i r, const TileClass *edge, + const TileClass *fill) { RECT_FOREACH(r) const TileClass *tc = Rect2iIsAtEdge(r, _v) ? edge : fill; @@ -1509,10 +1530,10 @@ static void MapGenerateRandomExitArea(Map *map, const int mission) exit.Hidden = false; for (int i = 0; i < 10000 && (t == NULL || !TileCanWalk(t)); i++) { - exit.R.Pos.x = (rand() % (abs(map->Size.x) - EXIT_WIDTH - 1)); - exit.R.Size.x = EXIT_WIDTH + 1; - exit.R.Pos.y = (rand() % (abs(map->Size.y) - EXIT_HEIGHT - 1)); - exit.R.Size.y = EXIT_HEIGHT + 1; + exit.R.Size.x = MIN(map->Size.x - 2, EXIT_WIDTH + 1); + exit.R.Pos.x = (rand() % (abs(map->Size.x) - exit.R.Size.x)); + exit.R.Size.y = MIN(map->Size.y - 2, EXIT_HEIGHT + 1); + exit.R.Pos.y = (rand() % (abs(map->Size.y) - exit.R.Size.y)); // Check that the exit area is walkable t = MapGetTile(map, Rect2iCenter(exit.R)); }