Add random pickups #71

Adjust map objects in Cyberdogs campaign
This commit is contained in:
Cong
2023-12-21 22:15:22 +11:00
parent bdaa038178
commit b870ddb5df
15 changed files with 516 additions and 300 deletions

View File

@@ -12,5 +12,5 @@
"MaxLives": 4,
"PlayerHP": 20,
"PlayerMaxHP": 50,
"Missions": 11
"Missions": 10
}

View File

@@ -1,141 +1,5 @@
{
"Missions": [{
"Title": "",
"Description": "",
"Type": "Classic",
"Width": 48,
"Height": 48,
"ExitStyle": "hazard",
"KeyStyle": "office",
"Objectives": [],
"Enemies": [3,
4,
5,
6,
7,
8],
"SpecialChars": [],
"MapObjectDensities": [],
"EnemyDensity": 20,
"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",
"Fists",
"Swarmer",
"Launcher",
"Pistol",
"Akimbo Pistols",
"Chainsaw",
"2xChainsaw",
"3xChainsaw",
"4xChainsaw",
"5xChainsaw",
"Fists",
"MiniGun",
"Launcher",
"Flamer",
"Powergun",
"Lazer",
"TurboLazer",
"Blaster",
"MegaGun",
"DumbGun",
"Gun"],
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
"Type": "Wall",
"Style": "steel",
"Mask": "848484ff",
"MaskAlt": "008400ff",
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
"Type": "Floor",
"Style": "recessed",
"Mask": "484848ff",
"MaskAlt": "008400ff",
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
"Type": "Floor",
"Style": "recessed",
"Mask": "707070ff",
"MaskAlt": "008400ff",
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
"Type": "Door",
"Style": "office",
"Mask": "ffffffff",
"MaskAlt": "ffffffff",
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true,
"DamageBullet": ""
}
},
"Walls": 11,
"WallLength": 14,
"CorridorWidth": 2,
"Rooms": {
"Count": 3,
"Min": 7,
"Max": 10,
"Edge": false,
"Overlap": false,
"Walls": 0,
"WallLength": 1,
"WallPad": 1
},
"Squares": 0,
"ExitEnabled": true,
"Doors": {
"Enabled": false,
"Min": 1,
"Max": 1,
"RandomPos": false
},
"Pillars": {
"Count": 0,
"Min": 1,
"Max": 1
}
},
{
"Title": "Mission 1",
"Description": "",
"Type": "Classic",
@@ -204,6 +68,14 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [{
"Pickup": "gun_Powergun",
"Density": 1
},
{
"Pickup": "ammo_Cells",
"Density": 1
}],
"EnemyDensity": 13,
"Weapons": ["Fists",
"Chainsaw",
@@ -339,25 +211,9 @@
5],
"SpecialChars": [],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -390,6 +246,14 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [{
"Pickup": "gun_Powergun",
"Density": 1
},
{
"Pickup": "ammo_Cells",
"Density": 1
}],
"EnemyDensity": 40,
"Weapons": ["Fists",
"Chainsaw",
@@ -520,25 +384,9 @@
5],
"SpecialChars": [],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -571,6 +419,7 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [],
"EnemyDensity": 50,
"Weapons": ["Fists",
"Chainsaw",
@@ -702,25 +551,9 @@
6],
"SpecialChars": [],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -753,6 +586,7 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [],
"EnemyDensity": 55,
"Weapons": ["Fists",
"Chainsaw",
@@ -899,25 +733,9 @@
5,
5],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -950,6 +768,7 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [],
"EnemyDensity": 13,
"Weapons": ["Fists",
"Chainsaw",
@@ -1089,25 +908,9 @@
4,
7],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -1140,6 +943,7 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [],
"EnemyDensity": 13,
"Weapons": ["Fists",
"Chainsaw",
@@ -1283,25 +1087,9 @@
7],
"SpecialChars": [],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -1334,6 +1122,7 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [],
"EnemyDensity": 65,
"Weapons": ["Fists",
"Chainsaw",
@@ -1482,25 +1271,9 @@
7,
8],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -1533,6 +1306,7 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [],
"EnemyDensity": 13,
"Weapons": ["Fists",
"Chainsaw",
@@ -1663,25 +1437,9 @@
7],
"SpecialChars": [],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -1714,6 +1472,7 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [],
"EnemyDensity": 70,
"Weapons": ["Fists",
"Chainsaw",
@@ -1850,25 +1609,9 @@
8],
"SpecialChars": [],
"MapObjectDensities": [{
"MapObject": "crate",
"Density": 20
},
{
"MapObject": "box",
"Density": 20
},
{
"MapObject": "fan",
"Density": 10
},
{
"MapObject": "cabinet_server",
"Density": 20
},
{
"MapObject": "box3_gray",
"Density": 20
},
{
"MapObject": "blood0",
"Density": 10
@@ -1901,6 +1644,7 @@
"MapObject": "bloodstain1",
"Density": 20
}],
"PickupDensities": [],
"EnemyDensity": 80,
"Weapons": ["Fists",
"Chainsaw",

View File

@@ -349,18 +349,24 @@ bool MapTryPlaceDestroyObject(
return MapTryPlaceOneObject(mb, pos, mo, ObjectiveToThing(objective), strict);
}
void MapPlacePickup(
const PickupClass *p, const struct vec2 pos, const int flags)
{
GameEvent e = GameEventNew(GAME_EVENT_ADD_PICKUP);
strcpy(e.u.AddPickup.PickupClass, p->Name);
e.u.AddPickup.ThingFlags = flags;
e.u.AddPickup.Pos = Vec2ToNet(pos);
GameEventsEnqueue(&gGameEvents, e);
}
void MapPlaceCollectible(
const Mission *m, const int objective, const struct vec2 pos)
{
const Objective *o = CArrayGet(&m->Objectives, objective);
GameEvent e = GameEventNew(GAME_EVENT_ADD_PICKUP);
// Pick a random pickup out of the available ones
const int i = RAND_INT(0, (int)o->u.Pickups.size - 1);
const PickupClass *p = *(const PickupClass **)CArrayGet(&o->u.Pickups, i);
strcpy(e.u.AddPickup.PickupClass, p->Name);
e.u.AddPickup.ThingFlags = ObjectiveToThing(objective);
e.u.AddPickup.Pos = Vec2ToNet(pos);
GameEventsEnqueue(&gGameEvents, e);
MapPlacePickup(p, pos, ObjectiveToThing(objective));
}
struct vec2 MapGenerateFreePosition(Map *map, const struct vec2i size)

View File

@@ -378,6 +378,17 @@ static json_t *SaveMissions(CArray *a)
json_insert_child(modsNode, modNode);
}
json_insert_pair_into_object(node, "MapObjectDensities", modsNode);
json_t *pdsNode = json_new_array();
for (int j = 0; j < (int)mission->PickupDensities.size; j++)
{
const PickupDensity *pd =
CArrayGet(&mission->PickupDensities, j);
json_t *pdNode = json_new_object();
AddStringPair(pdNode, "Pickup", pd->P->Name);
AddIntPair(pdNode, "Density", pd->Density);
json_insert_child(pdsNode, pdNode);
}
json_insert_pair_into_object(node, "PickupDensities", pdsNode);
AddIntPair(node, "EnemyDensity", mission->EnemyDensity);
json_insert_pair_into_object(

View File

@@ -22,7 +22,7 @@
This file incorporates work covered by the following copyright and
permission notice:
Copyright (c) 2013-2014, 2017-2022 Cong Xu
Copyright (c) 2013-2014, 2017-2023 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -433,7 +433,6 @@ void MapLoadDynamic(MapBuilder *mb)
MapStaticLoadDynamic(mb);
}
// Add map objects
CA_FOREACH(const MapObjectDensity, mod, mb->mission->MapObjectDensities)
for (int j = 0;
j < (mod->Density * mb->Map->Size.x * mb->Map->Size.y) / 1000; j++)
@@ -441,6 +440,19 @@ void MapLoadDynamic(MapBuilder *mb)
MapTryPlaceOneObject(mb, MapGetRandomTile(mb->Map), mod->M, 0, true);
}
CA_FOREACH_END()
CA_FOREACH(const PickupDensity, pd, mb->mission->PickupDensities)
for (int j = 0;
j < (pd->Density * mb->Map->Size.x * mb->Map->Size.y) / 5000; j++)
{
const struct vec2 v = MapGetRandomPos(mb->Map);
const struct vec2i size = svec2i(COLLECTABLE_W, COLLECTABLE_H);
if (!IsCollisionWithWall(v, size))
{
MapPlacePickup(pd->P, v, 0);
}
}
CA_FOREACH_END()
if (HasObjectives(gCampaign.Entry.Mode))
{

View File

@@ -22,7 +22,7 @@
This file incorporates work covered by the following copyright and
permission notice:
Copyright (c) 2013-2015, 2017-2022 Cong Xu
Copyright (c) 2013-2015, 2017-2023 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -89,6 +89,8 @@ bool MapTryPlaceOneObject(
const int extraFlags, const bool isStrictMode);
bool MapTryPlaceDestroyObject(
MapBuilder *mb, const Mission *m, const int objective, const struct vec2i pos, const bool strict);
void MapPlacePickup(
const PickupClass *p, const struct vec2 pos, const int flags);
// TODO: refactor
void MapPlaceCollectible(
const Mission *m, const int objective, const struct vec2 pos);

View File

@@ -1,7 +1,7 @@
/*
C-Dogs SDL
A port of the legendary (and fun) action/arcade cdogs.
Copyright (c) 2014-2017, 2019-2022 Cong Xu
Copyright (c) 2014-2017, 2019-2023 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -258,6 +258,22 @@ void LoadMissions(CArray *missions, json_t *missionsNode, int version)
}
}
}
json_t *pdsNode =
json_find_first_label(child, "PickupDensities");
if (pdsNode && pdsNode->child)
{
pdsNode = pdsNode->child;
for (json_t *pdNode = pdsNode->child; pdNode;
pdNode = pdNode->next)
{
PickupDensity pd;
pd.P = StrPickupClass(
json_find_first_label(pdNode, "Pickup")
->child->text);
LoadInt(&pd.Density, pdNode, "Density");
CArrayPushBack(&m.PickupDensities, &pd);
}
}
LoadInt(&m.EnemyDensity, child, "EnemyDensity");
LoadWeapons(
&m.Weapons, json_find_first_label(child, "Weapons")->child);

View File

@@ -144,6 +144,7 @@ void MissionInit(Mission *m)
CArrayInit(&m->Enemies, sizeof(int));
CArrayInit(&m->SpecialChars, sizeof(int));
CArrayInit(&m->MapObjectDensities, sizeof(MapObjectDensity));
CArrayInit(&m->PickupDensities, sizeof(PickupDensity));
CArrayInit(&m->Weapons, sizeof(const WeaponClass *));
m->Type = MAPTYPE_CLASSIC;
m->u.Classic.ExitEnabled = true;
@@ -195,6 +196,7 @@ void MissionCopy(Mission *dst, const Mission *src)
CArrayCopy(&dst->Enemies, &src->Enemies);
CArrayCopy(&dst->SpecialChars, &src->SpecialChars);
CArrayCopy(&dst->MapObjectDensities, &src->MapObjectDensities);
CArrayCopy(&dst->PickupDensities, &src->PickupDensities);
dst->EnemyDensity = src->EnemyDensity;
CArrayCopy(&dst->Weapons, &src->Weapons);
@@ -261,6 +263,7 @@ void MissionTerminate(Mission *m)
CArrayTerminate(&m->Enemies);
CArrayTerminate(&m->SpecialChars);
CArrayTerminate(&m->MapObjectDensities);
CArrayTerminate(&m->PickupDensities);
CArrayTerminate(&m->Weapons);
switch (m->Type)
{

View File

@@ -22,7 +22,7 @@
This file incorporates work covered by the following copyright and
permission notice:
Copyright (c) 2013-2017, 2019-2022 Cong Xu
Copyright (c) 2013-2017, 2019-2023 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -96,6 +96,11 @@ typedef struct
int Density;
} MapObjectDensity;
typedef struct
{
const PickupClass *P;
int Density;
} PickupDensity;
typedef struct
{
int Count;
int Min;
@@ -134,6 +139,7 @@ typedef struct
CArray Enemies; // of int (character index)
CArray SpecialChars; // of int
CArray MapObjectDensities; // of MapObjectDensity
CArray PickupDensities; // of PickupDensity
int EnemyDensity;
CArray Weapons; // of WeaponClass *

View File

@@ -22,7 +22,7 @@
This file incorporates work covered by the following copyright and
permission notice:
Copyright (c) 2013-2021 Cong Xu
Copyright (c) 2013-2021, 2023 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -310,6 +310,17 @@ static void DeleteItem(Mission *m, int idx)
CArrayDelete(&m->MapObjectDensities, idx);
}
static void DeletePickup(Mission *m, int idx)
{
if (idx >= (int)m->PickupDensities.size)
{
// Nothing to delete; do nothing
return;
}
idx = CLAMP(idx, 0, (int)m->PickupDensities.size);
CArrayDelete(&m->PickupDensities, idx);
}
static void AdjustYC(int *yc)
{
Mission *mission = CampaignGetCurrentMission(&gCampaign);
@@ -361,6 +372,14 @@ static void AdjustXC(int yc, int *xc)
*xc, 0, (int)mission->MapObjectDensities.size - 1);
}
break;
case YC_PICKUPS:
if (mission && mission->PickupDensities.size > 0)
{
*xc = CLAMP_OPPOSITE(
*xc, 0, (int)mission->PickupDensities.size - 1);
}
break;
default:
break;
@@ -555,6 +574,10 @@ static void Delete(int xc, int yc)
case YC_ITEMS:
DeleteItem(mission, xc);
break;
case YC_PICKUPS:
DeletePickup(mission, xc);
break;
default:
if (yc >= YC_OBJECTIVES)
@@ -1100,6 +1123,15 @@ static void InputInsert(int *xc, const int yc, Mission *mission)
*xc = (int)(mission->MapObjectDensities.size - 1);
}
break;
case YC_PICKUPS: {
PickupDensity pd;
pd.P = PickupClassGetById(&gPickupClasses, 0);
pd.Density = 0;
CArrayPushBack(&mission->PickupDensities, &pd);
*xc = (int)(mission->PickupDensities.size - 1);
}
break;
default:
if (yc >= YC_OBJECTIVES)

View File

@@ -1,7 +1,7 @@
/*
C-Dogs SDL
A port of the legendary (and fun) action/arcade cdogs.
Copyright (c) 2013-2016, 2019-2021 Cong Xu
Copyright (c) 2013-2016, 2019-2021, 2023 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -356,6 +356,21 @@ static const char *GetObjectCountStr(UIObject *o, void *v)
(int)gMission.missionData->MapObjectDensities.size);
return s;
}
static const char *GetPickupCountStr(UIObject *o, void *v)
{
static char s[128];
UNUSED(o);
UNUSED(v);
Mission *m = CampaignGetCurrentMission(&gCampaign);
if (!m)
{
return NULL;
}
sprintf(
s, "Pickups (%d)",
(int)gMission.missionData->PickupDensities.size);
return s;
}
typedef struct
{
Campaign *co;
@@ -416,6 +431,22 @@ static void MissionDrawMapItem(
svec2i_add(svec2i_add(pos, o->Pos), svec2i_scale_divide(o->Size, 2)),
mod, UIObjectIsHighlighted(o));
}
static void MissionDrawPickup(
UIObject *o, GraphicsDevice *g, struct vec2i pos, void *vData)
{
UNUSED(g);
MissionIndexData *data = vData;
if (!CampaignGetCurrentMission(data->co))
return;
const Mission *m = CampaignGetCurrentMission(data->co);
if (data->index >= (int)m->PickupDensities.size)
return;
const PickupDensity *pd =
CArrayGet(&m->PickupDensities, data->index);
DisplayPickupWithDensity(
svec2i_add(svec2i_add(pos, o->Pos), svec2i_scale_divide(o->Size, 2)),
pd, UIObjectIsHighlighted(o));
}
static void DrawStyleArea(
struct vec2i pos, const char *name, const Pic *pic, int idx, int count,
int isHighlighted)
@@ -635,6 +666,7 @@ static void DeactivateBrush(UIObject *o, void *data)
}
static UIObject *CreateMapItemObjs(Campaign *co, int dy);
static UIObject *CreatePickupObjs(Campaign *co, int dy);
static UIObject *CreateCharacterObjs(Campaign *co, int dy);
static UIObject *CreateSpecialCharacterObjs(Campaign *co, int dy);
@@ -976,6 +1008,18 @@ static UIObject *CreateEditorObjs(Campaign *co, EditorBrush *brush)
"Shift+click to change amounts");
UIObjectAddChild(o2, CreateMapItemObjs(co, pos.y));
UIObjectAddChild(c, o2);
pos.y += th;
o2 = UIObjectCopy(o);
o2->u.LabelFunc = GetPickupCountStr;
o2->Data = NULL;
o2->Id = YC_PICKUPS;
o2->Pos = pos;
CSTRDUP(
o2->Tooltip,
"Use Insert/" KMOD_CMD_NAME "+i, Delete/" KMOD_CMD_NAME "+d and PageUp/PageDown\n"
"Shift+click to change amounts");
UIObjectAddChild(o2, CreatePickupObjs(co, pos.y));
UIObjectAddChild(c, o2);
UIObjectDestroy(o);
@@ -1007,7 +1051,7 @@ static UIObject *CreateMapItemObjs(Campaign *co, int dy)
for (int i = 0; i < 32; i++) // TODO: no limit to objects
{
const int x = 10 + i * 20;
// Drop-down menu for objective type
// Drop-down menu for map object type
UIObject *o2 = UIObjectCopy(o);
o2->Id2 = i;
CMALLOC(o2->Data, sizeof(MissionIndexData));
@@ -1105,6 +1149,97 @@ static void DrawMapItem(
}
}
typedef struct
{
Campaign *C;
int Idx;
PickupClass *P;
} PickupIndexData;
static EditorResult MissionChangePickupDensity(void *vData, int d);
static bool PickupObjFunc(UIObject *o, PickupClass *p, void *vData);
static UIObject *CreatePickupObjs(Campaign *co, int dy)
{
UIObject *c = UIObjectCreate(UITYPE_NONE, 0, svec2i_zero(), svec2i_zero());
c->Flags = UI_ENABLED_WHEN_PARENT_HIGHLIGHTED_ONLY;
UIObject *o =
UIObjectCreate(UITYPE_CUSTOM, 0, svec2i_zero(), svec2i(20, 40));
o->u.CustomDrawFunc = MissionDrawPickup;
o->ChangeFuncAlt = MissionChangePickupDensity;
o->Flags = UI_LEAVE_YC;
for (int i = 0; i < 32; i++) // TODO: no limit to pickups
{
const int x = 10 + i * 20;
// Drop-down menu for pickup type
UIObject *o2 = UIObjectCopy(o);
o2->Id2 = i;
CMALLOC(o2->Data, sizeof(MissionIndexData));
o2->IsDynamicData = 1;
((MissionIndexData *)o2->Data)->co = co;
((MissionIndexData *)o2->Data)->index = i;
o2->Pos = svec2i(x, Y_ABS - dy);
CSTRDUP(
o2->Tooltip,
"Click: change pickup; Shift+Click: change density");
UIObjectAddChild(
o2, CreateAddPickupObjs(
svec2i(o2->Size.x, o2->Size.y / 2), PickupObjFunc,
o2->Data, sizeof(PickupIndexData), false));
UIObjectAddChild(c, o2);
}
UIObjectDestroy(o);
return c;
}
static EditorResult MissionChangePickupDensity(void *vData, int d)
{
MissionIndexData *data = vData;
Mission *m = CampaignGetCurrentMission(data->co);
if (data->index >= (int)m->PickupDensities.size)
{
return EDITOR_RESULT_NONE;
}
PickupDensity *pd = CArrayGet(&m->PickupDensities, data->index);
pd->Density = CLAMP(pd->Density + d, 0, 512);
return EDITOR_RESULT_CHANGED;
}
static EditorResult MissionSetPickup(void *vData, int d);
static void DrawPickup(
UIObject *o, GraphicsDevice *g, struct vec2i pos, void *vData);
static bool PickupObjFunc(UIObject *o, PickupClass *p, void *vData)
{
o->ChangeFunc = MissionSetPickup;
o->u.CustomDrawFunc = DrawPickup;
MissionIndexData *data = vData;
((PickupIndexData *)o->Data)->C = data->co;
((PickupIndexData *)o->Data)->Idx = data->index;
((PickupIndexData *)o->Data)->P = p;
o->Tooltip = MakePickupTooltip(p);
return true;
}
static EditorResult MissionSetPickup(void *vData, int d)
{
UNUSED(d);
PickupIndexData *data = vData;
Mission *m = CampaignGetCurrentMission(data->C);
if (data->Idx >= (int)m->PickupDensities.size)
{
return EDITOR_RESULT_NONE;
}
PickupDensity *pd = CArrayGet(&m->PickupDensities, data->Idx);
pd->P = data->P;
return EDITOR_RESULT_CHANGED;
}
static void DrawPickup(
UIObject *o, GraphicsDevice *g, struct vec2i pos, void *vData)
{
UNUSED(g);
const PickupIndexData *data = vData;
DisplayPickup(
svec2i_add(svec2i_add(pos, o->Pos), svec2i_scale_divide(o->Size, 2)),
data->P);
}
static UIObject *CreateCharacterObjs(Campaign *co, int dy)
{
UIObject *c;

View File

@@ -22,7 +22,7 @@
This file incorporates work covered by the following copyright and
permission notice:
Copyright (c) 2013-2014, 2021 Cong Xu
Copyright (c) 2013-2014, 2021, 2023 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -60,7 +60,8 @@
#define YC_MISSIONLOOKS 4
#define YC_CHARACTERS 6
#define YC_SPECIALS 7
#define YC_ITEMS 9
#define YC_ITEMS 8
#define YC_PICKUPS 9
#define YC_OBJECTIVES 10
#define XC_MISSIONTITLE 0

View File

@@ -1,7 +1,7 @@
/*
C-Dogs SDL
A port of the legendary (and fun) action/arcade cdogs.
Copyright (c) 2014, 2016-2017, 2019-2021 Cong Xu
Copyright (c) 2014, 2016-2017, 2019-2021, 2023 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -58,6 +58,28 @@ void DisplayMapItemWithDensity(
FontStr(s, svec2i_add(pos, svec2i(-8, 5)));
}
void DisplayPickup(const struct vec2i pos, const PickupClass *p)
{
const Pic *pic = CPicGetPic(&p->Pic, 0);
PicRender(
pic, gGraphicsDevice.gameWindow.renderer, svec2i_subtract(pos, svec2i_scale_divide(pic->size, 2)),
colorWhite, 0, svec2_one(), SDL_FLIP_NONE, Rect2iZero());
}
void DisplayPickupWithDensity(
const struct vec2i pos, const PickupDensity *pd,
const bool isHighlighted)
{
DisplayPickup(pos, pd->P);
if (isHighlighted)
{
FontCh('>', svec2i_add(pos, svec2i(-8, -4)));
}
char s[10];
sprintf(s, "%d", pd->Density);
FontStr(s, svec2i_add(pos, svec2i(-8, 5)));
}
void DrawKey(UIObject *o, GraphicsDevice *g, struct vec2i pos, void *vData)
{
const IndexedEditorBrush *data = vData;
@@ -367,6 +389,178 @@ static int CountAddMapItemSubObjs(const UIObject *o)
return count;
}
typedef struct
{
bool (*ObjFunc)(UIObject *, PickupClass *, void *);
void *Data;
// Data size required for pickup change checking
size_t DataSize;
struct vec2i GridItemSize;
struct vec2i GridSize;
struct vec2i PageOffset;
} CreateAddPickupObjsImplData;
static UIObject *CreateAddPickupObjsImpl(
struct vec2i pos, CreateAddPickupObjsImplData data);
UIObject *CreateAddPickupObjs(
const struct vec2i pos, bool (*objFunc)(UIObject *, PickupClass *, void *),
void *data, const size_t dataSize, const bool expandDown)
{
CreateAddPickupObjsImplData d;
d.ObjFunc = objFunc;
d.Data = data;
d.DataSize = dataSize;
d.GridItemSize = svec2i(TILE_WIDTH + 4, TILE_HEIGHT * 2 + 4);
d.GridSize = svec2i(8, 6);
if (expandDown)
{
d.PageOffset = svec2i(-5, 5);
}
else
{
d.PageOffset =
svec2i(-5, -d.GridItemSize.y * d.GridSize.y - FontH() - 5);
}
return CreateAddPickupObjsImpl(pos, d);
}
static void CreateAddPickupSubObjs(UIObject *c, void *vData);
static UIObject *CreateAddPickupObjsImpl(
struct vec2i pos, CreateAddPickupObjsImplData data)
{
UIObject *c = UIObjectCreate(UITYPE_CONTEXT_MENU, 0, pos, svec2i_zero());
c->OnFocusFunc = CreateAddPickupSubObjs;
c->IsDynamicData = true;
CMALLOC(c->Data, sizeof(CreateAddPickupObjsImplData));
memcpy(c->Data, &data, sizeof data);
return c;
}
static int CountAddPickupSubObjs(const UIObject *o);
static void CreateAddPickupSubObjs(UIObject *c, void *vData)
{
const CreateAddPickupObjsImplData *data = vData;
const int pageSize = data->GridSize.x * data->GridSize.y;
// Check if we need to recreate the objs
// TODO: this is a very heavyweight way to do it
int count = 0;
bool allChildrenSame = true;
for (int i = 0; i < PickupClassesCount(&gPickupClasses); i++)
{
PickupClass *p = PickupClassGetById(&gPickupClasses, i);
UIObject *o2 = UIObjectCreate(
UITYPE_CUSTOM, 0, svec2i_zero(), data->GridItemSize);
o2->IsDynamicData = true;
CCALLOC(o2->Data, data->DataSize);
if (!data->ObjFunc(o2, p, data->Data))
{
UIObjectDestroy(o2);
continue;
}
const int pageIdx = count / pageSize;
if (pageIdx >= (int)c->Children.size)
{
allChildrenSame = false;
break;
}
const UIObject **op = CArrayGet(&c->Children, pageIdx);
const UIObject **octx = CArrayGet(&(*op)->Children, 0);
const int idx = count % pageSize;
if (idx >= (int)(*octx)->Children.size)
{
allChildrenSame = false;
break;
}
const UIObject **oc = CArrayGet(&(*octx)->Children, idx);
if (memcmp(o2->Data, (*oc)->Data, data->DataSize) != 0)
{
allChildrenSame = false;
UIObjectDestroy(o2);
break;
}
count++;
UIObjectDestroy(o2);
}
int cCount = CountAddPickupSubObjs(c);
if (cCount == count && allChildrenSame)
{
return;
}
// Recreate the child UI objects
c->Highlighted = NULL;
UIObject **objs = c->Children.data;
for (int i = 0; i < (int)c->Children.size; i++, objs++)
{
UIObjectDestroy(*objs);
}
CArrayClear(&c->Children);
// Create pagination
int pageNum = 1;
UIObject *pageLabel = NULL;
UIObject *page = NULL;
UIObject *o =
UIObjectCreate(UITYPE_CUSTOM, 0, svec2i_zero(), data->GridItemSize);
const struct vec2i gridStart = svec2i_zero();
struct vec2i pos = svec2i_zero();
count = 0;
for (int i = 0; i < PickupClassesCount(&gPickupClasses); i++)
{
PickupClass *p = PickupClassGetById(&gPickupClasses, i);
UIObject *o2 = UIObjectCopy(o);
o2->IsDynamicData = true;
CCALLOC(o2->Data, data->DataSize);
if (!data->ObjFunc(o2, p, data->Data))
{
UIObjectDestroy(o2);
continue;
}
o2->Pos = pos;
if (count == 0)
{
pageLabel = UIObjectCreate(
UITYPE_LABEL, 0, svec2i((pageNum - 1) * 10, 0),
svec2i(10, FontH()));
char buf[32];
sprintf(buf, "%d", pageNum);
UIObjectSetDynamicLabel(pageLabel, buf);
page = UIObjectCreate(
UITYPE_CONTEXT_MENU, 0,
svec2i_add(data->PageOffset, pageLabel->Size), svec2i_zero());
UIObjectAddChild(pageLabel, page);
pageNum++;
}
UIObjectAddChild(page, o2);
pos.x += o->Size.x;
if (((count + 1) % data->GridSize.x) == 0)
{
pos.x = gridStart.x;
pos.y += o->Size.y;
}
count++;
if (count == pageSize)
{
count = 0;
pos = gridStart;
UIObjectAddChild(c, pageLabel);
}
}
if (pageLabel != NULL)
{
UIObjectAddChild(c, pageLabel);
}
UIObjectDestroy(o);
}
static int CountAddPickupSubObjs(const UIObject *o)
{
int count = 0;
CA_FOREACH(const UIObject *, page, o->Children)
const UIObject **ctx = CArrayGet(&(*page)->Children, 0);
count += (int)(*ctx)->Children.size;
CA_FOREACH_END()
return count;
}
char *MakeMapObjectTooltip(const MapObject *mo)
{
// Add a descriptive tooltip for the map object
@@ -427,6 +621,52 @@ void MapObjectGetExplosionGunNames(const MapObject *mo, char *buf, const char *s
}
}
char *MakePickupTooltip(const PickupClass *pc)
{
// Add a descriptive tooltip for the pickup
char buf[512];
strcpy(buf, pc->Name);
// Add effect descriptions
CA_FOREACH(const PickupEffect, pe, pc->Effects)
char peBuf[128];
switch (pe->Type)
{
case PICKUP_JEWEL:
sprintf(peBuf, "\n- Score: %d", pe->u.Score);
break;
case PICKUP_HEALTH:
sprintf(peBuf, "\n- Health: %d", pe->u.Health);
break;
case PICKUP_AMMO: {
const Ammo *a = AmmoGetById(&gAmmo, pe->u.Ammo.Id);
sprintf(peBuf, "\n- Ammo: %s %d", a->Name, (int)pe->u.Ammo.Amount);
}
break;
case PICKUP_KEYCARD:
sprintf(peBuf, "\n- Key: %s", KeycardStr(pe->u.Keys));
break;
case PICKUP_GUN: {
const WeaponClass *wc = IdWeaponClass(pe->u.GunId);
sprintf(peBuf, "\n- Weapon: %s", wc->name);
}
break;
case PICKUP_SHOW_MAP:
strcpy(peBuf, "\n- Show map");
break;
case PICKUP_LIVES:
sprintf(peBuf, "\n- Lives: %d", pe->u.Lives);
break;
default:
CASSERT(false, "Unknown pickup type");
break;
}
strcat(buf, peBuf);
CA_FOREACH_END()
char *tmp;
CSTRDUP(tmp, buf);
return tmp;
}
static EditorResult CloseChange(void *data, int d);
void CreateCloseLabel(UIObject *c, const struct vec2i pos)
{

View File

@@ -61,6 +61,10 @@ typedef struct
void DisplayMapItem(const struct vec2i pos, const MapObject *mo);
void DisplayMapItemWithDensity(
const struct vec2i pos, const MapObjectDensity *mod, const bool isHighlighted);
void DisplayPickup(const struct vec2i pos, const PickupClass *p);
void DisplayPickupWithDensity(
const struct vec2i pos, const PickupDensity *pd,
const bool isHighlighted);
void DrawKey(UIObject *o, GraphicsDevice *g, struct vec2i pos, void *vData);
void InsertMission(Campaign *co, Mission *mission, int idx);
@@ -75,11 +79,15 @@ UIObject *CreateCampaignSeedObj(const struct vec2i pos, Campaign *co);
UIObject *CreateAddMapItemObjs(
const struct vec2i pos, bool (*objFunc)(UIObject *, MapObject *, void *),
void *data, const size_t dataSize, const bool expandDown);
UIObject *CreateAddPickupObjs(
const struct vec2i pos, bool (*objFunc)(UIObject *, PickupClass *, void *),
void *data, const size_t dataSize, const bool expandDown);
UIObject *CreateAddPickupSpawnerObjs(
const struct vec2i pos, bool (*objFunc)(UIObject *, MapObject *, void *),
void *data, const size_t dataSize);
char *MakeMapObjectTooltip(const MapObject *mo);
char *MakePickupTooltip(const PickupClass *p);
void MapObjectGetPlacementFlagNames(const MapObject *mo, char *buf, const char *sep);
void MapObjectGetExplosionGunNames(const MapObject *mo, char *buf, const char *sep);

View File

@@ -362,7 +362,7 @@ static char *MakeMapObjectTooltipSimple(const MapObject *mo)
return tmp;
}
static char *MakePickupTooltip(const MapObject *mo);
static char *MakePickupSpawnerTooltip(const MapObject *mo);
static bool AddPickupSpawnerBrushObjFunc(
UIObject *o, MapObject *mo, void *vData)
{
@@ -376,10 +376,10 @@ static bool AddPickupSpawnerBrushObjFunc(
o->OnUnfocusFunc = DeactivateIndexedEditorBrush;
((IndexedEditorBrush *)o->Data)->Brush = vData;
((IndexedEditorBrush *)o->Data)->u.MapObject = mo;
o->Tooltip = MakePickupTooltip(mo);
o->Tooltip = MakePickupSpawnerTooltip(mo);
return true;
}
static char *MakePickupTooltip(const MapObject *mo)
static char *MakePickupSpawnerTooltip(const MapObject *mo)
{
char *tmp;
CASSERT(mo->u.PickupClass->Name != NULL, "No pickup name");