mirror of
https://github.com/cxong/cdogs-sdl.git
synced 2025-07-23 07:23:01 +02:00
Add basic item spawners (#329)
Add spawn item tech demo level Fix bug where items sometimes cannot be picked up if other things exist inside the same tile Add item spawn sound for ammo and health Fix bug where map items cannot be placed on drain tile Fix potential indexing bug with wrecks
This commit is contained in:
BIN
graphics/spawn_pad.png
Normal file
BIN
graphics/spawn_pad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 241 B |
4
graphics/spawn_pad.txt
Normal file
4
graphics/spawn_pad.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Adapted from Sci-Fi Object Set by Janna
|
||||
http://opengameart.org/content/sci-fi-object-set
|
||||
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
7
missions/techdemo/spawnertest.cdogscpn/campaign.json
Normal file
7
missions/techdemo/spawnertest.cdogscpn/campaign.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Version": 4,
|
||||
"Title": "Spawner Test",
|
||||
"Author": "Cong",
|
||||
"Description": "Testing some spawners",
|
||||
"Missions": 1
|
||||
}
|
3
missions/techdemo/spawnertest.cdogscpn/characters.json
Normal file
3
missions/techdemo/spawnertest.cdogscpn/characters.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Characters": []
|
||||
}
|
101
missions/techdemo/spawnertest.cdogscpn/missions.json
Normal file
101
missions/techdemo/spawnertest.cdogscpn/missions.json
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"Missions": [{
|
||||
"Title": "",
|
||||
"Description": "",
|
||||
"Type": "Static",
|
||||
"Width": 16,
|
||||
"Height": 16,
|
||||
"WallStyle": 2,
|
||||
"FloorStyle": 9,
|
||||
"RoomStyle": 9,
|
||||
"ExitStyle": 1,
|
||||
"KeyStyle": 0,
|
||||
"DoorStyle": 0,
|
||||
"Objectives": [],
|
||||
"Enemies": [],
|
||||
"SpecialChars": [],
|
||||
"MapObjectDensities": [],
|
||||
"EnemyDensity": 0,
|
||||
"Weapons": ["Knife",
|
||||
"Machine gun",
|
||||
"Grenades",
|
||||
"Flamer",
|
||||
"Shotgun",
|
||||
"Powergun",
|
||||
"Shrapnel bombs",
|
||||
"Molotovs",
|
||||
"Sniper rifle",
|
||||
"Prox. mine",
|
||||
"Dynamite",
|
||||
"Chemo bombs",
|
||||
"Petrify gun",
|
||||
"Browny gun",
|
||||
"Confusion bombs",
|
||||
"Chemo gun",
|
||||
"Pulse rifle",
|
||||
"Heatseeker",
|
||||
"Swarmer",
|
||||
"Launcher",
|
||||
"Pistol"],
|
||||
"Song": "",
|
||||
"WallColor": 8,
|
||||
"FloorColor": 12,
|
||||
"RoomColor": 8,
|
||||
"AltColor": 9,
|
||||
"Tiles": "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
|
||||
"StaticItems": [{
|
||||
"MapObject": "Bullets spawner",
|
||||
"Positions": [[2,
|
||||
2]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Grenades spawner",
|
||||
"Positions": [[4,
|
||||
2]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Gas tank spawner",
|
||||
"Positions": [[6,
|
||||
2]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Shells spawner",
|
||||
"Positions": [[8,
|
||||
2]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Cells spawner",
|
||||
"Positions": [[10,
|
||||
2]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Frag grenades spawner",
|
||||
"Positions": [[12,
|
||||
2]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Molotovs spawner",
|
||||
"Positions": [[2,
|
||||
4]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Mines spawner",
|
||||
"Positions": [[4,
|
||||
4],
|
||||
[10,
|
||||
4]]
|
||||
}],
|
||||
"StaticWrecks": [],
|
||||
"StaticCharacters": [],
|
||||
"StaticObjectives": [],
|
||||
"StaticKeys": [],
|
||||
"Start": [0,
|
||||
0],
|
||||
"Exit": {
|
||||
"Start": [0,
|
||||
0],
|
||||
"End": [0,
|
||||
0]
|
||||
}
|
||||
}]
|
||||
}
|
BIN
sounds/spawn_item.ogg
Normal file
BIN
sounds/spawn_item.ogg
Normal file
Binary file not shown.
3
sounds/spawn_item.txt
Normal file
3
sounds/spawn_item.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
alien_phaser.R.wav by torn.rugged.audio.35
|
||||
http://freesound.org/people/torn.rugged.audio.35/sounds/45935/
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
@@ -353,6 +353,7 @@ static void CheckTrigger(const Vec2i tilePos)
|
||||
static Vec2i GetConstrainedFullPos(
|
||||
const Map *map, const Vec2i fromFull, const Vec2i toFull,
|
||||
const Vec2i size);
|
||||
static void CheckPickups(TActor *actor, const Vec2i realPos);
|
||||
bool TryMoveActor(TActor *actor, Vec2i pos)
|
||||
{
|
||||
CASSERT(!Vec2iEqual(actor->Pos, pos), "trying to move to same position");
|
||||
@@ -457,17 +458,7 @@ bool TryMoveActor(TActor *actor, Vec2i pos)
|
||||
|
||||
CheckTrigger(Vec2iToTile(realPos));
|
||||
|
||||
if (actor->playerIndex >= 0)
|
||||
{
|
||||
target = GetItemOnTileInCollision(
|
||||
&actor->tileItem, realPos, 0,
|
||||
CalcCollisionTeam(1, actor),
|
||||
IsPVP(gCampaign.Entry.Mode));
|
||||
if (target && target->kind == KIND_PICKUP)
|
||||
{
|
||||
PickupPickup(actor, CArrayGet(&gPickups, target->id));
|
||||
}
|
||||
}
|
||||
CheckPickups(actor, realPos);
|
||||
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_MOVE);
|
||||
e.u.ActorMove.Id = actor->tileItem.id;
|
||||
@@ -573,6 +564,27 @@ static Vec2i GetConstrainedFullPos(
|
||||
// All alternative movements are in collision; don't move
|
||||
return fromFull;
|
||||
}
|
||||
// Check if the player can pickup any item
|
||||
static void CheckPickupFunc(TTileItem *ti, void *data);
|
||||
static void CheckPickups(TActor *actor, const Vec2i realPos)
|
||||
{
|
||||
// NPCs can't pickup
|
||||
if (actor->playerIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CollideAllItems(
|
||||
&actor->tileItem, realPos, 0, CalcCollisionTeam(true, actor),
|
||||
IsPVP(gCampaign.Entry.Mode), CheckPickupFunc, actor);
|
||||
}
|
||||
static void CheckPickupFunc(TTileItem *ti, void *data)
|
||||
{
|
||||
TActor *actor = data;
|
||||
if (ti->kind == KIND_PICKUP)
|
||||
{
|
||||
PickupPickup(actor, CArrayGet(&gPickups, ti->id));
|
||||
}
|
||||
}
|
||||
|
||||
void ActorHeal(TActor *actor, int health)
|
||||
{
|
||||
|
@@ -223,6 +223,7 @@ bool CollisionIsOnSameTeam(
|
||||
!isPVP;
|
||||
}
|
||||
|
||||
// TODO: refactor away into CollideAllItems, use bool continue on callback
|
||||
TTileItem *GetItemOnTileInCollision(
|
||||
const TTileItem *item, Vec2i pos, int mask, CollisionTeam team,
|
||||
const bool isPVP)
|
||||
@@ -284,7 +285,7 @@ void CollideAllItems(
|
||||
if (!CollisionIsOnSameTeam(ti, team, isPVP))
|
||||
{
|
||||
if (item != ti &&
|
||||
(ti->flags & mask) &&
|
||||
(mask == 0 || (ti->flags & mask)) &&
|
||||
ItemsCollide(item, ti, pos))
|
||||
{
|
||||
func(ti, data);
|
||||
|
@@ -128,7 +128,16 @@ static void HandleGameEvent(
|
||||
}
|
||||
break;
|
||||
case GAME_EVENT_ADD_HEALTH_PICKUP:
|
||||
MapPlaceHealth(e->u.AddHealthPickup);
|
||||
{
|
||||
MapPlaceHealth(e->u.AddHealthPickup);
|
||||
// Play a spawn sound
|
||||
GameEvent sound = GameEventNew(GAME_EVENT_SOUND_AT);
|
||||
sound.u.SoundAt.Sound = StrSound("spawn_item");
|
||||
sound.u.SoundAt.Pos = e->u.AddHealthPickup.Pos;
|
||||
HandleGameEvent(
|
||||
&sound, hud, shake, healthSpawner, ammoSpawners,
|
||||
eventHandlers);
|
||||
}
|
||||
break;
|
||||
case GAME_EVENT_TAKE_HEALTH_PICKUP:
|
||||
{
|
||||
@@ -152,45 +161,54 @@ static void HandleGameEvent(
|
||||
}
|
||||
break;
|
||||
case GAME_EVENT_ADD_AMMO_PICKUP:
|
||||
MapPlaceAmmo(e->u.AddAmmoPickup);
|
||||
{
|
||||
MapPlaceAmmo(e->u.AddAmmoPickup);
|
||||
// Play a spawn sound
|
||||
GameEvent sound = GameEventNew(GAME_EVENT_SOUND_AT);
|
||||
sound.u.SoundAt.Sound = StrSound("spawn_item");
|
||||
sound.u.SoundAt.Pos = e->u.AddAmmoPickup.Pos;
|
||||
HandleGameEvent(
|
||||
&sound, hud, shake, healthSpawner, ammoSpawners,
|
||||
eventHandlers);
|
||||
}
|
||||
break;
|
||||
case GAME_EVENT_TAKE_AMMO_PICKUP:
|
||||
{
|
||||
const PlayerData *p =
|
||||
CArrayGet(&gPlayerDatas, e->u.Heal.PlayerIndex);
|
||||
if (IsPlayerAlive(p))
|
||||
{
|
||||
TActor *a = CArrayGet(&gActors, p->Id);
|
||||
if (!a->isInUse)
|
||||
const PlayerData *p =
|
||||
CArrayGet(&gPlayerDatas, e->u.Heal.PlayerIndex);
|
||||
if (IsPlayerAlive(p))
|
||||
{
|
||||
break;
|
||||
TActor *a = CArrayGet(&gActors, p->Id);
|
||||
if (!a->isInUse)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ActorAddAmmo(a, e->u.AddAmmo.AddAmmo);
|
||||
if (e->u.AddAmmo.IsRandomSpawned)
|
||||
{
|
||||
PowerupSpawnerRemoveOne(
|
||||
CArrayGet(ammoSpawners, e->u.AddAmmo.AddAmmo.Id));
|
||||
}
|
||||
// TODO: some sort of text effect showing ammo grab
|
||||
}
|
||||
ActorAddAmmo(a, e->u.AddAmmo.AddAmmo);
|
||||
if (e->u.AddAmmo.IsRandomSpawned)
|
||||
{
|
||||
PowerupSpawnerRemoveOne(
|
||||
CArrayGet(ammoSpawners, e->u.AddAmmo.AddAmmo.Id));
|
||||
}
|
||||
// TODO: some sort of text effect showing ammo grab
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GAME_EVENT_USE_AMMO:
|
||||
{
|
||||
const PlayerData *p =
|
||||
CArrayGet(&gPlayerDatas, e->u.UseAmmo.PlayerIndex);
|
||||
if (IsPlayerAlive(p))
|
||||
{
|
||||
TActor *a = CArrayGet(&gActors, p->Id);
|
||||
if (!a->isInUse)
|
||||
const PlayerData *p =
|
||||
CArrayGet(&gPlayerDatas, e->u.UseAmmo.PlayerIndex);
|
||||
if (IsPlayerAlive(p))
|
||||
{
|
||||
break;
|
||||
TActor *a = CArrayGet(&gActors, p->Id);
|
||||
if (!a->isInUse)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ActorAddAmmo(a, e->u.UseAmmo.UseAmmo);
|
||||
// TODO: some sort of text effect showing ammo usage
|
||||
}
|
||||
ActorAddAmmo(a, e->u.UseAmmo.UseAmmo);
|
||||
// TODO: some sort of text effect showing ammo usage
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GAME_EVENT_MOBILE_OBJECT_REMOVE:
|
||||
MobObjDestroy(e->u.MobileObjectRemoveId);
|
||||
break;
|
||||
|
@@ -433,7 +433,7 @@ bool MapTryPlaceOneObject(
|
||||
Tile *t = MapGetTile(map, v);
|
||||
unsigned short iMap = IMapGet(map, v);
|
||||
|
||||
bool isEmpty = !(t->flags & ~MAPTILE_IS_NORMAL_FLOOR) && TileIsClear(t);
|
||||
const bool isEmpty = TileIsClear(t);
|
||||
if (isStrictMode && !MapObjectIsTileOKStrict(
|
||||
mo, iMap, isEmpty,
|
||||
IMapGet(map, Vec2iNew(v.x, v.y - 1)),
|
||||
@@ -480,9 +480,8 @@ void MapPlaceWreck(Map *map, const Vec2i v, const MapObject *mo)
|
||||
{
|
||||
Tile *t = MapGetTile(map, v);
|
||||
unsigned short iMap = IMapGet(map, v);
|
||||
bool isEmpty = !(t->flags & ~MAPTILE_IS_NORMAL_FLOOR) && TileIsClear(t);
|
||||
if (!MapObjectIsTileOK(
|
||||
mo, iMap, isEmpty, IMapGet(map, Vec2iNew(v.x, v.y - 1))))
|
||||
mo, iMap, TileIsClear(t), IMapGet(map, Vec2iNew(v.x, v.y - 1))))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -604,6 +603,7 @@ void MapPlaceKey(
|
||||
void MapPlaceAmmo(AddAmmoPickup a)
|
||||
{
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, a.Id);
|
||||
// TODO: can explicitly link ammo to pickup without string lookup
|
||||
char buf[256];
|
||||
sprintf(buf, "ammo_%s", ammo->Name);
|
||||
const int id = PickupAdd(Vec2iReal2Full(a.Pos), StrPickupClass(buf));
|
||||
@@ -622,10 +622,10 @@ static void MapPlaceCard(Map *map, int keyIndex, int map_access)
|
||||
t = MapGetTile(map, v);
|
||||
iMap = IMapGet(map, v);
|
||||
tBelow = MapGetTile(map, Vec2iNew(v.x, v.y + 1));
|
||||
if (!(t->flags & ~MAPTILE_IS_NORMAL_FLOOR) && TileIsClear(t) &&
|
||||
if (TileIsClear(t) &&
|
||||
(iMap & 0xF00) == map_access &&
|
||||
(iMap & MAP_MASKACCESS) == MAP_ROOM &&
|
||||
!(tBelow->flags & ~MAPTILE_IS_NORMAL_FLOOR) && TileIsClear(tBelow))
|
||||
TileIsClear(tBelow))
|
||||
{
|
||||
MapPlaceKey(map, &gMission, v, keyIndex);
|
||||
return;
|
||||
|
@@ -141,6 +141,8 @@ int MapNewLoadArchive(const char *filename, CampaignSetting *c)
|
||||
MapObjectsLoadJSON(&gMapObjects.CustomClasses, root);
|
||||
}
|
||||
|
||||
MapObjectsLoadAmmoSpawners(&gMapObjects, &gAmmo);
|
||||
|
||||
|
||||
root = ReadPhysFSJSON(filename, "missions.json");
|
||||
if (root == NULL)
|
||||
|
@@ -239,6 +239,7 @@ void MapObjectsLoadJSON(CArray *classes, json_t *root)
|
||||
static void LoadMapObject(MapObject *m, json_t *node)
|
||||
{
|
||||
memset(m, 0, sizeof *m);
|
||||
m->Idx = -1;
|
||||
|
||||
LoadInt(&m->Idx, node, "Index");
|
||||
m->Name = GetString(node, "Name");
|
||||
@@ -299,6 +300,37 @@ static void AddDestructibles(MapObjects *m, const CArray *classes)
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadAmmoSpawners(
|
||||
MapObjects *classes, const CArray *ammo, const int fromId);
|
||||
void MapObjectsLoadAmmoSpawners(MapObjects *classes, const AmmoClasses *ammo)
|
||||
{
|
||||
LoadAmmoSpawners(classes, &ammo->Ammo, 0);
|
||||
LoadAmmoSpawners(classes, &ammo->CustomAmmo, ammo->Ammo.size);
|
||||
}
|
||||
static void LoadAmmoSpawners(
|
||||
MapObjects *classes, const CArray *ammo, const int fromId)
|
||||
{
|
||||
for (int i = 0; i < (int)ammo->size; i++)
|
||||
{
|
||||
const Ammo *a = CArrayGet(ammo, i);
|
||||
MapObject m;
|
||||
memset(&m, 0, sizeof m);
|
||||
m.Idx = -1;
|
||||
char buf[256];
|
||||
sprintf(buf, "%s spawner", a->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_AMMO_SPAWNER;
|
||||
m.u.AmmoPickupId = i + fromId;
|
||||
CArrayPushBack(&classes->CustomClasses, &m);
|
||||
}
|
||||
}
|
||||
|
||||
void MapObjectsClear(CArray *classes)
|
||||
{
|
||||
for (int i = 0; i < (int)classes->size; i++)
|
||||
@@ -417,3 +449,8 @@ bool MapObjectIsTileOKStrict(
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MapObjectIsAmmoSpawner(const MapObject *mo)
|
||||
{
|
||||
return mo->Type == MAP_OBJECT_TYPE_AMMO_SPAWNER;
|
||||
}
|
||||
|
@@ -49,6 +49,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <json/json.h>
|
||||
#include "ammo.h"
|
||||
#include "pic_manager.h"
|
||||
|
||||
typedef enum
|
||||
@@ -71,6 +72,13 @@ typedef struct
|
||||
const Pic *Pic;
|
||||
Vec2i Offset;
|
||||
} MapObjectPic;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MAP_OBJECT_TYPE_NORMAL,
|
||||
MAP_OBJECT_TYPE_AMMO_SPAWNER
|
||||
} MapObjectType;
|
||||
|
||||
// A static map object, taking up an entire tile
|
||||
typedef struct
|
||||
{
|
||||
@@ -80,9 +88,17 @@ typedef struct
|
||||
MapObjectPic Wreck;
|
||||
Vec2i Size;
|
||||
int Health;
|
||||
// Guns that are fired when this map object is destroyed
|
||||
// i.e. explosion on destruction
|
||||
CArray DestroyGuns; // of const GunDescription *
|
||||
// Bit field composed of bits shifted by PlacementFlags
|
||||
int Flags;
|
||||
MapObjectType Type;
|
||||
union
|
||||
{
|
||||
// TODO: custom respawn rate?
|
||||
int AmmoPickupId;
|
||||
} u;
|
||||
} MapObject;
|
||||
typedef struct
|
||||
{
|
||||
@@ -107,6 +123,7 @@ MapObject *RandomBloodMapObject(const MapObjects *mo);
|
||||
|
||||
void MapObjectsInit(MapObjects *classes, const char *filename);
|
||||
void MapObjectsLoadJSON(CArray *classes, json_t *root);
|
||||
void MapObjectsLoadAmmoSpawners(MapObjects *classes, const AmmoClasses *ammo);
|
||||
void MapObjectsClear(CArray *classes);
|
||||
void MapObjectsTerminate(MapObjects *classes);
|
||||
int MapObjectsCount(const MapObjects *classes);
|
||||
@@ -122,3 +139,5 @@ bool MapObjectIsTileOKStrict(
|
||||
const MapObject *obj, const unsigned short tile, const bool isEmpty,
|
||||
const unsigned short tileAbove, const unsigned short tileBelow,
|
||||
const int numWallsAdjacent, const int numWallsAround);
|
||||
|
||||
bool MapObjectIsAmmoSpawner(const MapObject *mo);
|
||||
|
@@ -70,6 +70,7 @@
|
||||
|
||||
#define SOUND_LOCK_MOBILE_OBJECT 12
|
||||
#define SHOT_IMPULSE_DIVISOR 25
|
||||
#define AMMO_SPAWNER_RESPAWN_TICKS (FPS_FRAMELIMIT * 30)
|
||||
|
||||
CArray gObjs;
|
||||
CArray gMobObjs;
|
||||
@@ -441,6 +442,37 @@ bool ObjIsDangerous(const TObject *o)
|
||||
return o->Class->DestroyGuns.size > 0;
|
||||
}
|
||||
|
||||
void UpdateObjects(const int ticks)
|
||||
{
|
||||
for (int i = 0; i < (int)gObjs.size; i++)
|
||||
{
|
||||
TObject *obj = CArrayGet(&gObjs, i);
|
||||
if (!obj->isInUse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (obj->Class->Type)
|
||||
{
|
||||
case MAP_OBJECT_TYPE_AMMO_SPAWNER:
|
||||
// TODO: move counter only if ammo taken
|
||||
obj->counter -= ticks;
|
||||
if (obj->counter <= 0)
|
||||
{
|
||||
obj->counter += AMMO_SPAWNER_RESPAWN_TICKS;
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ADD_AMMO_PICKUP);
|
||||
e.u.AddAmmoPickup.Pos =
|
||||
Vec2iNew(obj->tileItem.x, obj->tileItem.y);
|
||||
e.u.AddAmmoPickup.Id = obj->Class->u.AmmoPickupId;
|
||||
GameEventsEnqueue(&gGameEvents, e);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MobileObjectUpdate(TMobileObject *obj, int ticks)
|
||||
{
|
||||
|
@@ -64,6 +64,7 @@ typedef struct
|
||||
{
|
||||
const MapObject *Class;
|
||||
int Health;
|
||||
int counter;
|
||||
TTileItem tileItem;
|
||||
bool isInUse;
|
||||
} TObject;
|
||||
@@ -112,6 +113,8 @@ void ObjDestroy(int id);
|
||||
// Check if this object is dangerous; i.e. on destruction will explode
|
||||
bool ObjIsDangerous(const TObject *o);
|
||||
|
||||
void UpdateObjects(const int ticks);
|
||||
|
||||
void UpdateMobileObjects(int ticks);
|
||||
void MobObjsInit(void);
|
||||
void MobObjsTerminate(void);
|
||||
|
@@ -105,7 +105,9 @@ bool TileIsNormalFloor(Tile *t)
|
||||
}
|
||||
bool TileIsClear(Tile *t)
|
||||
{
|
||||
return t->things.size == 0;
|
||||
return
|
||||
!(t->flags & ~(MAPTILE_IS_NORMAL_FLOOR | MAPTILE_IS_DRAINAGE)) &&
|
||||
t->things.size == 0;
|
||||
}
|
||||
bool TileHasCharacter(Tile *t)
|
||||
{
|
||||
|
@@ -396,7 +396,9 @@ EditorResult EditorBrushStartPainting(EditorBrush *b, Mission *m, int isMain)
|
||||
if (isMain)
|
||||
{
|
||||
if (MissionStaticTryAddWreck(
|
||||
m, IndexMapObject(b->ItemIndex), b->Pos))
|
||||
m,
|
||||
StrMapObject(CArrayGet(&gMapObjects.Destructibles, b->ItemIndex)),
|
||||
b->Pos))
|
||||
{
|
||||
return EDITOR_RESULT_CHANGED_AND_RELOAD;
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (c) 2013-2014, Cong Xu
|
||||
Copyright (c) 2013-2015, Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -96,14 +96,26 @@ static void DrawMapItem(
|
||||
static void DrawWreck(
|
||||
UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
|
||||
{
|
||||
UNUSED(g);
|
||||
IndexedEditorBrush *data = vData;
|
||||
const IndexedEditorBrush *data = vData;
|
||||
const char **name = CArrayGet(&gMapObjects.Destructibles, data->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(&gGraphicsDevice, pic, Vec2iAdd(pos, offset));
|
||||
Blit(g, pic, Vec2iAdd(pos, offset));
|
||||
}
|
||||
static void DrawAmmo(
|
||||
UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
|
||||
{
|
||||
const IndexedEditorBrush *data = vData;
|
||||
const MapObject *mo = IndexMapObject(data->ItemIndex);
|
||||
DisplayMapItem(
|
||||
Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2)), mo);
|
||||
const Pic *ammoPic = AmmoGetById(&gAmmo, mo->u.AmmoPickupId)->Pic;
|
||||
pos = Vec2iMinus(pos, Vec2iScaleDiv(ammoPic->size, 2));
|
||||
Blit(
|
||||
g, ammoPic,
|
||||
Vec2iAdd(Vec2iAdd(pos, o->Pos), Vec2iScaleDiv(o->Size, 2)));
|
||||
}
|
||||
static void DrawCharacter(
|
||||
UIObject *o, GraphicsDevice *g, Vec2i pos, void *vData)
|
||||
@@ -200,6 +212,7 @@ static void DeactivateEditorBrushAndCampaignBrush(void *data)
|
||||
|
||||
static UIObject *CreateAddMapItemObjs(Vec2i pos, EditorBrush *brush);
|
||||
static UIObject *CreateAddWreckObjs(Vec2i pos, EditorBrush *brush);
|
||||
static UIObject *CreateAddAmmoSpawnerObjs(Vec2i pos, EditorBrush *brush);
|
||||
static UIObject *CreateAddCharacterObjs(
|
||||
Vec2i pos, EditorBrush *brush, CampaignOptions *co);
|
||||
static UIObject *CreateAddObjectiveObjs(
|
||||
@@ -240,6 +253,12 @@ UIObject *CreateAddItemObjs(
|
||||
UIObjectAddChild(c, o2);
|
||||
pos.y += th;
|
||||
o2 = UIObjectCopy(o);
|
||||
o2->Label = "Ammo spawner";
|
||||
o2->Pos = pos;
|
||||
UIObjectAddChild(o2, CreateAddAmmoSpawnerObjs(o2->Size, brush));
|
||||
UIObjectAddChild(c, o2);
|
||||
pos.y += th;
|
||||
o2 = UIObjectCopy(o);
|
||||
o2->Label = "Character";
|
||||
o2->Pos = pos;
|
||||
UIObjectAddChild(o2, CreateAddCharacterObjs(o2->Size, brush, co));
|
||||
@@ -260,7 +279,7 @@ UIObject *CreateAddItemObjs(
|
||||
UIObjectDestroy(o);
|
||||
return c;
|
||||
}
|
||||
static void AddPlacementFlagTooltip(UIObject *o2, const int idx);
|
||||
static void AddPlacementFlagTooltip(const MapObject *mo, UIObject *o2);
|
||||
static UIObject *CreateAddMapItemObjs(Vec2i pos, EditorBrush *brush)
|
||||
{
|
||||
UIObject *o2;
|
||||
@@ -268,7 +287,7 @@ static UIObject *CreateAddMapItemObjs(Vec2i pos, EditorBrush *brush)
|
||||
|
||||
UIObject *o = UIObjectCreate(
|
||||
UITYPE_CUSTOM, 0,
|
||||
Vec2iZero(), Vec2iNew(TILE_WIDTH/* * 2*/ + 4, TILE_HEIGHT * /*3*/2 + 4));
|
||||
Vec2iZero(), Vec2iNew(TILE_WIDTH + 4, TILE_HEIGHT * 2 + 4));
|
||||
o->ChangeFunc = BrushSetBrushTypeAddMapItem;
|
||||
o->u.CustomDrawFunc = DrawMapItem;
|
||||
o->OnFocusFunc = ActivateIndexedEditorBrush;
|
||||
@@ -277,6 +296,12 @@ static UIObject *CreateAddMapItemObjs(Vec2i pos, EditorBrush *brush)
|
||||
const int width = 8;
|
||||
for (int i = 0; i < MapObjectsCount(&gMapObjects); i++)
|
||||
{
|
||||
// Only add map objects that are not ammo spawners
|
||||
const MapObject *mo = IndexMapObject(i);
|
||||
if (MapObjectIsAmmoSpawner(mo))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
o2 = UIObjectCopy(o);
|
||||
o2->IsDynamicData = 1;
|
||||
CMALLOC(o2->Data, sizeof(IndexedEditorBrush));
|
||||
@@ -290,16 +315,15 @@ static UIObject *CreateAddMapItemObjs(Vec2i pos, EditorBrush *brush)
|
||||
pos.x = 0;
|
||||
pos.y += o->Size.y;
|
||||
}
|
||||
AddPlacementFlagTooltip(o2, i);
|
||||
AddPlacementFlagTooltip(mo, o2);
|
||||
}
|
||||
|
||||
UIObjectDestroy(o);
|
||||
return c;
|
||||
}
|
||||
static void AddPlacementFlagTooltip(UIObject *o2, const int idx)
|
||||
static void AddPlacementFlagTooltip(const MapObject *mo, UIObject *o2)
|
||||
{
|
||||
// Add a descriptive tooltip for the map object
|
||||
const MapObject *mo = IndexMapObject(idx);
|
||||
char buf[512];
|
||||
// Construct text representing the placement flags
|
||||
char pfBuf[128];
|
||||
@@ -380,6 +404,54 @@ static UIObject *CreateAddWreckObjs(Vec2i pos, EditorBrush *brush)
|
||||
UIObjectDestroy(o);
|
||||
return c;
|
||||
}
|
||||
static UIObject *CreateAddAmmoSpawnerObjs(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 = BrushSetBrushTypeAddMapItem;
|
||||
o->u.CustomDrawFunc = DrawAmmo;
|
||||
o->OnFocusFunc = ActivateIndexedEditorBrush;
|
||||
o->OnUnfocusFunc = DeactivateIndexedEditorBrush;
|
||||
pos = Vec2iZero();
|
||||
const int width = 4;
|
||||
int count = 0;
|
||||
for (int i = 0; i < MapObjectsCount(&gMapObjects); i++)
|
||||
{
|
||||
// Only add map objects that are ammo spawners
|
||||
const MapObject *mo = IndexMapObject(i);
|
||||
if (!MapObjectIsAmmoSpawner(mo))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
o2 = UIObjectCopy(o);
|
||||
o2->IsDynamicData = true;
|
||||
CMALLOC(o2->Data, sizeof(IndexedEditorBrush));
|
||||
((IndexedEditorBrush *)o2->Data)->Brush = brush;
|
||||
((IndexedEditorBrush *)o2->Data)->ItemIndex = i;
|
||||
o2->Pos = pos;
|
||||
UIObjectAddChild(c, o2);
|
||||
pos.x += o->Size.x;
|
||||
if (((count + 1) % width) == 0)
|
||||
{
|
||||
pos.x = 0;
|
||||
pos.y += o->Size.y;
|
||||
}
|
||||
CSTRDUP(o2->Tooltip, AmmoGetById(&gAmmo, mo->u.AmmoPickupId)->Name);
|
||||
count++;
|
||||
}
|
||||
|
||||
UIObjectDestroy(o);
|
||||
if (count == 0)
|
||||
{
|
||||
UIObjectDestroy(c);
|
||||
c = NULL;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
static void CreateAddCharacterSubObjs(UIObject *c, void *vData);
|
||||
static UIObject *CreateAddCharacterObjs(
|
||||
Vec2i pos, EditorBrush *brush, CampaignOptions *co)
|
||||
|
@@ -2,7 +2,7 @@
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
|
||||
Copyright (c) 2013-2014, Cong Xu
|
||||
Copyright (c) 2013-2015, Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -132,6 +132,10 @@ void UIObjectDestroy(UIObject *o)
|
||||
|
||||
void UIObjectAddChild(UIObject *o, UIObject *c)
|
||||
{
|
||||
if (c == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CArrayPushBack(&o->Children, &c);
|
||||
c->Parent = o;
|
||||
assert(o->Type != UITYPE_TAB && "need special add child for TAB type");
|
||||
|
@@ -600,6 +600,7 @@ static GameLoopResult RunGameUpdate(void *data)
|
||||
}
|
||||
|
||||
UpdateAllActors(ticksPerFrame);
|
||||
UpdateObjects(ticksPerFrame);
|
||||
UpdateMobileObjects(ticksPerFrame);
|
||||
ParticlesUpdate(&gParticles, ticksPerFrame);
|
||||
|
||||
|
Reference in New Issue
Block a user