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:
Cong
2015-02-23 21:02:16 +11:00
parent 4b37e338c9
commit 228468b5aa
21 changed files with 381 additions and 58 deletions

BIN
graphics/spawn_pad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

4
graphics/spawn_pad.txt Normal file
View 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/

View File

@@ -0,0 +1,7 @@
{
"Version": 4,
"Title": "Spawner Test",
"Author": "Cong",
"Description": "Testing some spawners",
"Missions": 1
}

View File

@@ -0,0 +1,3 @@
{
"Characters": []
}

View 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

Binary file not shown.

3
sounds/spawn_item.txt Normal file
View 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/

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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");

View File

@@ -600,6 +600,7 @@ static GameLoopResult RunGameUpdate(void *data)
}
UpdateAllActors(ticksPerFrame);
UpdateObjects(ticksPerFrame);
UpdateMobileObjects(ticksPerFrame);
ParticlesUpdate(&gParticles, ticksPerFrame);