From cb89d8bcf3aa02c156b29118a3d862bbb00749e5 Mon Sep 17 00:00:00 2001 From: Cong Date: Wed, 29 Nov 2023 23:17:57 +1100 Subject: [PATCH] AI melee guns #71 --- .../cyberdogs.cdogscpn/characters.json | 6 + src/cdogs/actors.c | 13 ++- src/cdogs/character.c | 11 ++ src/cdogs/character.h | 1 + src/cdogsed/char_editor.c | 109 +++++++++++++++++- 5 files changed, 136 insertions(+), 4 deletions(-) diff --git a/missions/custom/techdemo/cyberdogs.cdogscpn/characters.json b/missions/custom/techdemo/cyberdogs.cdogscpn/characters.json index 1174ac25a..a73ea90d5 100644 --- a/missions/custom/techdemo/cyberdogs.cdogscpn/characters.json +++ b/missions/custom/techdemo/cyberdogs.cdogscpn/characters.json @@ -80,6 +80,7 @@ "Glasses": "ff1616ff", "speed": 192, "Gun": "DumbGun", + "Melee": "Fists", "maxHealth": 15, "flags": 167773184, "probabilityToMove": 25, @@ -101,6 +102,7 @@ "Glasses": "ff1616ff", "speed": 384, "Gun": "Lazer", + "Melee": "Fists", "maxHealth": 12, "flags": 167773184, "probabilityToMove": 50, @@ -122,6 +124,7 @@ "Glasses": "020000ff", "speed": 128, "Gun": "Gun", + "Melee": "Fists", "maxHealth": 60, "flags": 167773184, "probabilityToMove": 25, @@ -143,6 +146,7 @@ "Glasses": "ff1616ff", "speed": 512, "Gun": "Lazer", + "Melee": "Fists", "maxHealth": 12, "flags": 167773184, "probabilityToMove": 25, @@ -165,6 +169,7 @@ "Glasses": "fcfcfcff", "speed": 256, "Gun": "DumbGun", + "Melee": "Fists", "maxHealth": 15, "flags": 167773184, "probabilityToMove": 25, @@ -187,6 +192,7 @@ "Glasses": "ff1616ff", "speed": 256, "Gun": "TurboLazer", + "Melee": "Fists", "maxHealth": 60, "flags": 167773184, "probabilityToMove": 50, diff --git a/src/cdogs/actors.c b/src/cdogs/actors.c index e8ad29b8b..03b45523b 100644 --- a/src/cdogs/actors.c +++ b/src/cdogs/actors.c @@ -466,8 +466,10 @@ static bool CanMeleeTarget( if (!w->Gun || WeaponClassCanShoot(w->Gun)) return false; // Check for melee damage if we are the owner of the actor - if ((gCampaign.IsClient || a->PlayerUID < 0) && - !ActorIsLocalPlayer(a->uid)) + // Is client: must be local player + // Not client: AI or local player + if (!ActorIsLocalPlayer(a->uid) && + (gCampaign.IsClient || a->PlayerUID >= 0)) return false; // Check if any barrel can fire and melee for (int barrel = 0; barrel < WeaponClassNumBarrels(w->Gun); barrel++) @@ -1121,7 +1123,7 @@ 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) : + actor->PlayerUID < 0 ? (actor->flags & FLAGS_MOVE_AND_SHOOT) : ( ConfigGetEnum(&gConfig, "Game.FireMoveStyle") != FIREMOVE_STOP || !actor->hasShot || @@ -1724,6 +1726,11 @@ TActor *ActorAdd(NActorAdd aa) Weapon gun = WeaponCreate(c->Gun); actor->guns[0] = gun; actor->gunIndex = 0; + if (c->Melee) + { + Weapon melee = WeaponCreate(c->Melee); + actor->guns[MELEE_SLOT] = melee; + } } actor->health = aa.Health; actor->action = ACTORACTION_MOVING; diff --git a/src/cdogs/character.c b/src/cdogs/character.c index cc9043c4c..cc71160f1 100644 --- a/src/cdogs/character.c +++ b/src/cdogs/character.c @@ -209,6 +209,13 @@ void CharacterLoadJSON( tmp = GetString(child, "Gun"); ch->Gun = StrWeaponClass(tmp); CFREE(tmp); + tmp = NULL; + LoadStr(&tmp, child, "Melee"); + if (tmp != NULL) + { + ch->Melee = StrWeaponClass(tmp); + CFREE(tmp); + } LoadInt(&ch->maxHealth, child, "maxHealth"); int flags; LoadInt(&flags, child, "flags"); @@ -277,6 +284,10 @@ bool CharacterSave(CharacterStore *s, const char *path) AddColorPair(node, "Glasses", c->Colors.Glasses); AddIntPair(node, "speed", (int)(c->speed * 256)); json_insert_pair_into_object(node, "Gun", json_new_string(c->Gun->name)); + if (c->Melee != NULL) + { + json_insert_pair_into_object(node, "Melee", json_new_string(c->Melee->name)); + } AddIntPair(node, "maxHealth", c->maxHealth); AddIntPair(node, "flags", c->flags); if (c->Drop != NULL) diff --git a/src/cdogs/character.h b/src/cdogs/character.h index de17eb463..c924feef5 100644 --- a/src/cdogs/character.h +++ b/src/cdogs/character.h @@ -49,6 +49,7 @@ typedef struct char *HeadParts[HEAD_PART_COUNT]; float speed; const WeaponClass *Gun; + const WeaponClass *Melee; int maxHealth; unsigned int flags; CharColors Colors; diff --git a/src/cdogsed/char_editor.c b/src/cdogsed/char_editor.c index 819212f5a..ea926a841 100644 --- a/src/cdogsed/char_editor.c +++ b/src/cdogsed/char_editor.c @@ -44,12 +44,14 @@ typedef struct char *CharacterClassNames; char *HeadPartNames[HEAD_PART_COUNT]; char *GunNames; + char *MeleeNames; char *PickupNames; CArray texidsChars; // of GLuint[BODY_PART_COUNT] GLuint texidsPreview[BODY_PART_COUNT]; CArray texIdsCharClasses; // of GLuint CArray texIdsHeadParts[HEAD_PART_COUNT]; // of GLuint CArray texIdsGuns; // of GLuint + CArray texIdsMelees; // of GLuint CArray texIdsPickups; // of GLuint Animation anim; direction_e previewDir; @@ -76,11 +78,14 @@ static const char *IndexGlassesName(const int i) return IndexHeadPartName(i, HEAD_PART_GLASSES); } static const char *IndexGunName(const int i); +static const char *IndexMeleeName(const int i); static const char *IndexPickupName(const int i); static const WeaponClass *IndexWeaponClassReal(const int i); +static const WeaponClass *IndexWeaponClassMelee(const int i); static const PickupClass *IndexPickupClass(const int i); static int NumCharacterClasses(void); static int NumGuns(void); +static int NumMelees(void); static int NumPickups(void); static void AddCharacterTextures(EditorContext *ec); static bool Draw(SDL_Window *win, struct nk_context *ctx, void *data); @@ -118,6 +123,7 @@ void CharEditor( GetClassNames(gPicManager.headPartNames[hp].size, *indexHeadPartFuncs[hp]); } ec.GunNames = GetClassNames(NumGuns(), IndexGunName); + ec.MeleeNames = GetClassNames(NumMelees(), IndexMeleeName); ec.PickupNames = GetClassNames(NumPickups(), IndexPickupName); CArrayInit(&ec.texidsChars, sizeof(GLuint) * BODY_PART_COUNT); @@ -151,6 +157,14 @@ void CharEditor( const WeaponClass *wc = IndexWeaponClassReal(_ca_index); LoadTexFromPic(*texid, wc->Icon); CA_FOREACH_END() + + TexArrayInit(&ec.texIdsMelees, NumMelees()); + CA_FOREACH(const GLuint, texid, ec.texIdsMelees) + const WeaponClass *wc = IndexWeaponClassMelee(_ca_index); + if (wc == NULL) + continue; + LoadTexFromPic(*texid, wc->Icon); + CA_FOREACH_END() TexArrayInit(&ec.texIdsPickups, NumPickups()); CA_FOREACH(const GLuint, texid, ec.texIdsPickups) @@ -174,6 +188,7 @@ void CharEditor( CFREE(ec.HeadPartNames[hp]); } CFREE(ec.GunNames); + CFREE(ec.MeleeNames); CFREE(ec.PickupNames); glDeleteTextures( (GLsizei)(BODY_PART_COUNT * ec.texidsChars.size), ec.texidsChars.data); @@ -189,6 +204,7 @@ void CharEditor( DELTEX(ec.texIdsHeadParts[hp]); } DELTEX(ec.texIdsGuns); + DELTEX(ec.texIdsMelees); DELTEX(ec.texIdsPickups); } @@ -211,6 +227,11 @@ static const char *IndexGunName(const int i) const WeaponClass *wc = IndexWeaponClassReal(i); return wc ? wc->name : NULL; } +static const char *IndexMeleeName(const int i) +{ + const WeaponClass *wc = IndexWeaponClassMelee(i); + return wc ? wc->name : "(None)"; +} static const char *IndexPickupName(const int i) { const PickupClass *pc = IndexPickupClass(i); @@ -244,6 +265,38 @@ static const WeaponClass *IndexWeaponClassReal(const int i) CASSERT(false, "cannot find gun"); return NULL; } +static const WeaponClass *IndexWeaponClassMelee(const int i) +{ + if (i == 0) + { + return NULL; + } + int j = 1; + CA_FOREACH(WeaponClass, wc, gWeaponClasses.Guns) + if (!wc->IsRealGun || wc->Type != GUNTYPE_MELEE) + { + continue; + } + if (j == i) + { + return wc; + } + j++; + CA_FOREACH_END() + CA_FOREACH(WeaponClass, wc, gWeaponClasses.CustomGuns) + if (!wc->IsRealGun || wc->Type != GUNTYPE_MELEE) + { + continue; + } + if (j == i) + { + return wc; + } + j++; + CA_FOREACH_END() + CASSERT(false, "cannot find melee"); + return NULL; +} static const PickupClass *IndexPickupClass(const int i) { if (i == 0) @@ -292,6 +345,23 @@ static int NumGuns(void) CA_FOREACH_END() return total; } +static int NumMelees(void) +{ + int total = 0; + CA_FOREACH(const WeaponClass, wc, gWeaponClasses.Guns) + if (wc->IsRealGun && wc->Type == GUNTYPE_MELEE) + { + total++; + } + CA_FOREACH_END() + CA_FOREACH(const WeaponClass, wc, gWeaponClasses.CustomGuns) + if (wc->IsRealGun && wc->Type == GUNTYPE_MELEE) + { + total++; + } + CA_FOREACH_END() + return total; +} static int NumPickups(void) { return (int)(gPickupClasses.Classes.size + gPickupClasses.CustomClasses.size + gPickupClasses.KeyClasses.size + 1); @@ -324,6 +394,38 @@ static int GunIndex(const WeaponClass *wc) CASSERT(false, "cannot find gun"); return -1; } +static int MeleeIndex(const WeaponClass *wc) +{ + if (wc == NULL) + { + return 0; + } + int j = 1; + CA_FOREACH(const WeaponClass, wc2, gWeaponClasses.Guns) + if (!wc2->IsRealGun || wc2->Type != GUNTYPE_MELEE) + { + continue; + } + if (wc == wc2) + { + return j; + } + j++; + CA_FOREACH_END() + CA_FOREACH(const WeaponClass, wc2, gWeaponClasses.CustomGuns) + if (!wc2->IsRealGun || wc2->Type != GUNTYPE_MELEE) + { + continue; + } + if (wc == wc2) + { + return j; + } + j++; + CA_FOREACH_END() + CASSERT(false, "cannot find melee"); + return -1; +} static int PickupIndex(const PickupClass *pc) { if (pc == NULL) @@ -352,6 +454,7 @@ static int PickupIndex(const PickupClass *pc) } j++; CA_FOREACH_END() + CASSERT(false, "cannot find pickup"); return -1; } @@ -600,7 +703,7 @@ static bool Draw(SDL_Window *win, struct nk_context *ctx, void *data) if (nk_begin( ctx, "Attributes", - nk_rect(280, (float)charStoreSize.y + PAD, 250, 280), + nk_rect(280, (float)charStoreSize.y + PAD, 250, 310), NK_WINDOW_BORDER | NK_WINDOW_TITLE)) { // Speed (256 = 100%) @@ -637,6 +740,10 @@ static bool Draw(SDL_Window *win, struct nk_context *ctx, void *data) ctx, ec, "Gun:", ec->texIdsGuns.data, ec->GunNames, GunIndex(ec->Char->Gun), NumGuns()); ec->Char->Gun = IndexWeaponClassReal(selectedGun); + const int selectedMelee = DrawClassSelection( + ctx, ec, "Melee:", ec->texIdsMelees.data, ec->MeleeNames, + MeleeIndex(ec->Char->Melee), NumMelees()); + ec->Char->Melee = IndexWeaponClassMelee(selectedMelee); nk_layout_row_dynamic(ctx, ROW_HEIGHT, 1); nk_property_int(