欢迎光临
我们一直在努力

整活天花板!我给 C 语言课设加了「铠甲勇士打怪系统」,属性克制 + 场景讨伐,玩到停不下来

谁懂啊家人们!当初被 C 语言课设 “影院管理系统” 逼到挠头的时候,我盯着满屏的movie结构体和ticket订单,突然灵光一闪 —— 与其写枯燥的 “购票选座”,不如把整个系统魔改成「铠甲勇士召唤打怪系统」!

从最初只是想 “应付课设”,到后来越写越上头,我给系统加了怪兽场景、属性克制、铠甲破损维修、战斗记录存档…… 尤其是这个battleMonster()函数,直接把原本单调的 “订单管理” 升级成了有血有肉的 “铠甲讨伐怪兽” 核心玩法。现在打开控制台,输入指令就能选刑天铠甲去希望市打怪兽,属性克制能直接秒杀,授权等级不够还会被怪兽打崩铠甲,玩起来比小游戏还上头!

今天就把这个「铠甲勇士打怪核心函数」battleMonster()的完整开发思路、代码拆解、踩坑记录全扒出来,从场景选择到属性克制,从铠甲出战到战斗结算,每一行代码都带着 “整活魂”,新手也能跟着复刻,让你的 C 语言课设直接卷赢全班!

先明确核心:battleMonster()是整个铠甲勇士管理系统的玩法核心,也是我从 “影院系统” 魔改的灵魂函数 —— 原本影院系统里 “选择电影场次→选座位” 的逻辑,被我替换成 “选择怪兽场景→选讨伐怪兽→选出战铠甲→属性克制战斗”,全程贴合铠甲勇士的世界观,既保留了 C 语言课设要求的 “链表、结构体、条件判断、文件持久化” 等核心考点,又玩出了独一份的趣味。

先贴完整可运行代码(带详细注释,新手直接复制就能跑):

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// ====================== 常量定义(铠甲勇士世界观适配)======================
// 铠甲状态
#define ARMOR_IDLE 0 // 闲置(可出战)
#define ARMOR_IN_BATTLE 1 // 战斗中
#define ARMOR_REPAIRING 2 // 维修中
#define ARMOR_DAMAGED 3 // 破损(战败导致)

// 属性定义(克制关系:火克风、风克土、土克水、水克雷、雷克火)
#define ATTR_FIRE 1 // 火属性
#define ATTR_WATER 2 // 水属性
#define ATTR_THUNDER 3 // 雷属性
#define ATTR_EARTH 4 // 土属性
#define ATTR_WIND 5 // 风属性

// 怪兽场景枚举(铠一/铠二经典场景)
typedef enum {
SCENE_KAI1_CITY, // 铠一-城市市区
SCENE_KAI1_MOUNTAIN, // 铠一-郊外山林
SCENE_KAI1_OCEAN, // 铠一-沿海区域
SCENE_KAI1_ANCIENT, // 铠一-远古遗迹
SCENE_KAI2_HOPE_CITY, // 铠二-希望市
SCENE_KAI2_ARES, // 铠二-阿瑞斯星球
SCENE_KAI2_OTHER // 铠二-其他区域
} MonsterScene;

// ====================== 结构体定义(魔改影院系统核心)======================
// 铠甲结构体(原movie结构体)
typedef struct movie {
int id; // 铠甲ID
char title[50]; // 铠甲名称(刑天、飞影、金刚等)
char type[30]; // 铠甲类型(刑天小队、飞影小队等)
int attr; // 铠甲属性(火/水/雷/土/风)
float price; // 授权费用(元/小时,对应铠甲等级)
int status; // 铠甲状态(闲置/战斗中/维修/破损)
struct movie *next; // 链表指针
} movie;

// 怪兽结构体(新增核心结构体)
typedef struct monster {
int id; // 怪兽ID
char name[50]; // 怪兽名称(路法、巴鲁、库忿斯等)
int attr; // 怪兽属性
int danger; // 危险等级(1-10,对应铠甲授权等级)
MonsterScene scene; // 出没场景
int isDefeated; // 是否被封印(0-未封印 1-已封印)
struct monster *next; // 链表指针
} monster;

// 订单结构体(关联铠甲授权+战斗记录)
typedef struct order {
int id; // 订单ID
int movieId; // 关联铠甲ID
char movieName[50]; // 铠甲名称
float totalPrice; // 授权总费用
int quantity; // 剩余授权时长(小时)
int userId; // 召唤人ID
int monsterId; // 讨伐怪兽ID(新增)
int battleResult; // 战斗结果(1-胜利 0-失败 -1-未战斗)
struct order *next; // 链表指针
} order;

// 用户结构体(召唤人)
typedef struct user {
int id;
char name[20]; // 召唤人姓名
} User;

// ====================== 全局变量(简化版,新手友好)======================
movie *movieList = NULL; // 铠甲链表头
monster *monsterList = NULL; // 怪兽链表头
order *orderList = NULL; // 授权订单链表头
User *currentUser = NULL; // 当前登录召唤人

// ====================== 辅助函数(模块化封装,课设加分)======================
// 清屏函数(适配Windows/Linux)
void clearScreen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}

// 属性转换:枚举值转中文(避免重复写三目运算符)
char* attrToString(int attr) {
static char attrName[20];
switch(attr) {
case ATTR_FIRE: strcpy(attrName, "火属性"); break;
case ATTR_WATER: strcpy(attrName, "水属性"); break;
case ATTR_THUNDER: strcpy(attrName, "雷属性"); break;
case ATTR_EARTH: strcpy(attrName, "土属性"); break;
case ATTR_WIND: strcpy(attrName, "风属性"); break;
default: strcpy(attrName, "未知属性");
}
return attrName;
}

// 场景转换:枚举值转中文(贴合铠甲勇士IP)
char* sceneToString(MonsterScene scene) {
static char sceneName[30];
switch(scene) {
case SCENE_KAI1_CITY: strcpy(sceneName, "铠一-城市市区"); break;
case SCENE_KAI1_MOUNTAIN: strcpy(sceneName, "铠一-郊外山林"); break;
case SCENE_KAI1_OCEAN: strcpy(sceneName, "铠一-沿海区域"); break;
case SCENE_KAI1_ANCIENT: strcpy(sceneName, "铠一-远古遗迹"); break;
case SCENE_KAI2_HOPE_CITY: strcpy(sceneName, "铠二-希望市"); break;
case SCENE_KAI2_ARES: strcpy(sceneName, "铠二-阿瑞斯星球"); break;
case SCENE_KAI2_OTHER: strcpy(sceneName, "铠二-其他区域"); break;
default: strcpy(sceneName, "未知场景");
}
return sceneName;
}

// 数据持久化(保存铠甲/怪兽/订单数据到文件,课设核心加分项)
void save_to_file() {
// 这里省略具体文件操作代码,后续会单独讲,核心是把链表数据写入txt/bin文件
// 新手可先写空函数,重点实现打怪逻辑,后续补全
printf("数据已保存!\\n");
}

// ====================== 核心函数:battleMonster() 铠甲讨伐怪兽 ======================
void battleMonster() {
clearScreen(); // 清屏:优化控制台交互体验,避免界面杂乱

// 第一步:权限校验——未登录无法打怪(基础安全逻辑)
if (currentUser == NULL) {
printf("⚠️ 请先登录铠甲召唤系统!未授权召唤人无法出战!\\n");
getchar(); getchar(); // 等待按键:吸收回车+暂停,避免界面一闪而过
clearScreen();
return;
}

// 第二步:校验是否授权铠甲——没铠甲怎么打怪?(业务逻辑闭环)
order* userOrder = orderList;
int hasArmor = 0; // 标记是否有授权铠甲
while (userOrder != NULL) {
// 筛选当前登录用户的授权订单
if (userOrder->userId == currentUser->id) {
hasArmor = 1; // 找到授权记录,标记为有铠甲
break; // 找到即终止遍历,优化效率
}
userOrder = userOrder->next;
}
// 无授权铠甲时的友好提示(引导用户去授权,而非单纯报错)
if (hasArmor == 0) {
printf("❌ 你还没有授权任何铠甲!快去【召唤授权】模块申请刑天/飞影铠甲吧~\\n");
getchar(); getchar();
clearScreen();
return;
}

// 第三步:选择怪兽出没场景(整活核心:贴合铠甲勇士剧情)
printf("\\n🎮 ===== 选择讨伐场次(怪兽出没场景)=====\\n");
printf("1: 铠一-城市市区(炎龙铠甲主场)\\n2: 铠一-郊外山林(风鹰铠甲主场)\\n3: 铠一-沿海区域(黑犀铠甲主场)\\n");
printf("4: 铠一-远古遗迹(地虎铠甲主场)\\n5: 铠二-希望市(刑天铠甲主场)\\n6: 铠二-阿瑞斯星球(修罗铠甲主场)\\n7: 铠二-其他区域\\n");
printf("请选择要讨伐的场次:");
int sceneChoice;
scanf("%d", &sceneChoice);
MonsterScene targetScene; // 选中的目标场景
// 场景选择逻辑(输入校验,避免无效值)
switch (sceneChoice) {
case 1: targetScene = SCENE_KAI1_CITY; break;
case 2: targetScene = SCENE_KAI1_MOUNTAIN; break;
case 3: targetScene = SCENE_KAI1_OCEAN; break;
case 4: targetScene = SCENE_KAI1_ANCIENT; break;
case 5: targetScene = SCENE_KAI2_HOPE_CITY; break;
case 6: targetScene = SCENE_KAI2_ARES; break;
case 7: targetScene = SCENE_KAI2_OTHER; break;
default: // 输入错误处理
clearScreen();
printf("❌ 输入错误!请选择1-7之间的场次!\\n");
getchar(); getchar();
clearScreen();
return;
}

// 第四步:筛选当前场景未封印的怪兽(核心链表遍历+条件筛选)
clearScreen(); // 清屏展示怪兽列表,界面更整洁
monster* tempMonster = monsterList;
int hasUndefeated = 0; // 标记是否有未封印怪兽
int monsterCount = 0; // 未封印怪兽数量
int monsterIds[100]; // 存储怪兽ID(方便用户选择)
monster* monsterListArr[100]; // 存储怪兽指针(避免重复遍历)

printf("\\n👹 ===== %s 场次可讨伐的怪兽(未封印)=====\\n", sceneToString(targetScene));
while (tempMonster != NULL) {
// 筛选条件:未封印 + 匹配当前场景
if (tempMonster->isDefeated == 0 && tempMonster->scene == targetScene) {
hasUndefeated = 1;
monsterCount++;
monsterIds[monsterCount – 1] = tempMonster->id;
monsterListArr[monsterCount – 1] = tempMonster;
// 格式化输出怪兽信息(UI美化,像游戏选怪界面)
printf("%d. 怪兽名称:%s | 属性:%s | 危险等级:%d级\\n",
monsterCount,
tempMonster->name,
attrToString(tempMonster->attr),
tempMonster->danger);
}
tempMonster = tempMonster->next;
}
// 无未封印怪兽的提示(贴合剧情:该区域已被清理)
if (hasUndefeated == 0) {
printf("✅ 该场次暂无未封印的怪兽!你已守护这片区域~\\n");
getchar(); getchar();
clearScreen();
return;
}

// 第五步:选择要讨伐的怪兽(输入校验,避免越界)
int monsterChoice;
printf("请选择要讨伐的怪兽序号(1-%d):", monsterCount);
scanf("%d", &monsterChoice);
// 输入合法性校验(新手常漏,容易导致数组越界崩溃)
if (monsterChoice < 1 || monsterChoice > monsterCount) {
clearScreen();
printf("❌ 输入错误!请选择1-%d之间的序号!\\n", monsterCount);
getchar(); getchar();
clearScreen();
return;
}
// 定位选中的目标怪兽(通过数组快速获取,避免重复遍历链表)
monster* targetMonster = monsterListArr[monsterChoice – 1];

// 第六步:选择出战铠甲(筛选当前用户闲置铠甲)
clearScreen();
printf("\\n🛡️ ===== 选择出战铠甲 =====\\n");
userOrder = orderList; // 重置订单指针,重新遍历
int armorCount = 0; // 可出战铠甲数量
movie* armorList[100]; // 存储可出战铠甲指针

while (userOrder != NULL) {
if (userOrder->userId == currentUser->id) {
// 遍历铠甲链表,匹配订单关联的铠甲
movie* p = movieList;
while (p != NULL) {
// 筛选条件:订单关联铠甲 + 铠甲状态为闲置(可出战)
if (p->id == userOrder->movieId && p->status == ARMOR_IDLE) {
armorCount++;
armorList[armorCount – 1] = p;
// 格式化输出铠甲信息(展示核心属性,方便选择)
printf("%d. 铠甲名称:%s | 属性:%s | 剩余授权时长:%d小时\\n",
armorCount,
p->title,
attrToString(p->attr),
userOrder->quantity);
break; // 找到对应铠甲即终止内层遍历,优化效率
}
p = p->next;
}
}
userOrder = userOrder->next;
}
// 无可出战铠甲的提示(说明原因,而非单纯报错)
if (armorCount == 0) {
printf("❌ 你暂无闲置铠甲可出战!(原因:已出战/维修中/破损)\\n");
getchar(); getchar();
clearScreen();
return;
}

// 第七步:确认出战铠甲(输入校验)
int armorChoice;
printf("请选择出战的铠甲序号(1-%d):", armorCount);
scanf("%d", &armorChoice);
if (armorChoice < 1 || armorChoice > armorCount) {
clearScreen();
printf("❌ 输入错误!请选择1-%d之间的序号!\\n", armorCount);
getchar(); getchar();
clearScreen();
return;
}
// 定位选中的出战铠甲
movie* battleArmor = armorList[armorChoice – 1];

// 第八步:核心战斗逻辑(属性克制+等级判断,整活灵魂)
clearScreen();
printf("\\n⚔️ ===== 铠甲出战!缉捕怪兽 =====\\n");
printf("📍 场次:%s | 🛡️ %s VS 👹 %s 战斗开始!\\n",
sceneToString(targetScene), battleArmor->title, targetMonster->name);

int battleResult = 0; // 战斗结果:0-失败 1-胜利
// 核心规则1:属性克制(贴合铠甲勇士五行相克设定)
if (
(battleArmor->attr == ATTR_FIRE && targetMonster->attr == ATTR_WIND) || // 火克风
(battleArmor->attr == ATTR_WIND && targetMonster->attr == ATTR_EARTH) || // 风克土
(battleArmor->attr == ATTR_EARTH && targetMonster->attr == ATTR_WATER) || // 土克水
(battleArmor->attr == ATTR_WATER && targetMonster->attr == ATTR_THUNDER) || // 水克雷
(battleArmor->attr == ATTR_THUNDER && targetMonster->attr == ATTR_FIRE) // 雷克火
) {
battleResult = 1; // 克制则胜利
printf("🔥 【属性克制】%s对%s造成致命伤害!克制效果拉满!\\n", battleArmor->title, targetMonster->name);
}
// 核心规则2:同属性对决(看铠甲授权等级,即price)
else if (battleArmor->attr == targetMonster->attr) {
// 授权费用≥怪兽危险等级*2 → 胜利(授权等级足够)
if (battleArmor->price >= targetMonster->danger * 2) {
battleResult = 1;
printf("⚡ 【同属性对决】%s凭借高授权等级碾压%s!\\n", battleArmor->title, targetMonster->name);
}
else {
battleResult = 0; // 授权等级不足 → 失败
printf("💔 【同属性对决】%s授权等级不足,被%s压制!\\n", battleArmor->title, targetMonster->name);
}
}
// 核心规则3:属性被克制 → 直接失败
else {
battleResult = 0;
printf("❌ 【属性被克制】%s属性被克制,不敌%s!\\n", battleArmor->title, targetMonster->name);
}

// 第九步:战斗结算(更新铠甲/怪兽状态)
if (battleResult == 1) {
// 胜利:怪兽被封印,铠甲恢复闲置
targetMonster->isDefeated = 1;
battleArmor->status = ARMOR_IDLE;
printf("🎉 战斗胜利!%s已被%s封印!守护成功!\\n", targetMonster->name, battleArmor->title);
}
else {
// 失败:铠甲破损,需要维修
battleArmor->status = ARMOR_DAMAGED;
printf("💥 战斗失败!%s破损严重,需要前往维修站维修!\\n", battleArmor->title);
}

// 第十步:更新订单战斗记录(关联怪兽ID+战斗结果)
userOrder = orderList; // 重置订单指针
while (userOrder != NULL) {
// 匹配当前用户+当前出战铠甲的订单
if (userOrder->userId == currentUser->id && userOrder->movieId == battleArmor->id) {
userOrder->monsterId = targetMonster->id; // 关联讨伐的怪兽ID
userOrder->battleResult = battleResult; // 记录战斗结果
break; // 找到即终止,优化效率
}
userOrder = userOrder->next;
}

// 第十一步:数据持久化(保存战斗结果,避免重启丢失)
save_to_file();
printf("\\n🔚 战斗结束!按任意键返回菜单…\\n");
getchar(); getchar();
clearScreen();
}

// ====================== 测试函数(新手快速验证功能)======================
// 初始化测试数据(添加铠甲/怪兽/用户,方便直接运行)
void initTestData() {
// 添加测试用户(召唤人:李昊天)
currentUser = (User*)malloc(sizeof(User));
currentUser->id = 1;
strcpy(currentUser->name, "李昊天");

// 添加测试铠甲(刑天铠甲)
movie* xtArmor = (movie*)malloc(sizeof(movie));
xtArmor->id = 1;
strcpy(xtArmor->title, "刑天铠甲");
strcpy(xtArmor->type, "刑天小队");
xtArmor->attr = ATTR_FIRE;
xtArmor->price = 20.0; // 授权费用(对应等级)
xtArmor->status = ARMOR_IDLE;
xtArmor->next = NULL;
movieList = xtArmor;

// 添加测试怪兽(路法)
monster* lufaMonster = (monster*)malloc(sizeof(monster));
lufaMonster->id = 1;
strcpy(lufaMonster->name, "路法");
lufaMonster->attr = ATTR_WIND;
lufaMonster->danger = 9;
lufaMonster->scene = SCENE_KAI2_HOPE_CITY;
lufaMonster->isDefeated = 0;
lufaMonster->next = NULL;
monsterList = lufaMonster;

// 添加测试授权订单
order* testOrder = (order*)malloc(sizeof(order));
testOrder->id = 1;
testOrder->movieId = 1;
strcpy(testOrder->movieName, "刑天铠甲");
testOrder->totalPrice = 100.0;
testOrder->quantity = 10; // 剩余授权时长10小时
testOrder->userId = 1;
testOrder->monsterId = 0;
testOrder->battleResult = -1;
testOrder->next = NULL;
orderList = testOrder;
}

// 主函数(测试入口)
int main() {
initTestData(); // 初始化测试数据
battleMonster(); // 调用打怪函数
return 0;
}

我把battleMonster()函数拆成 11 个核心步骤,每个步骤既讲技术考点,又聊 “整活思路”,让你不仅能看懂代码,还能知道 “为什么这么改”“怎么改更有趣”:

步骤 1:权限校验 —— 未登录无法打怪(基础安全逻辑)

if (currentUser == NULL) {
printf("⚠️ 请先登录铠甲召唤系统!未授权召唤人无法出战!\\n");
getchar(); getchar();
clearScreen();
return;
}

技术考点:指针判空、函数提前返回、控制台交互;整活思路:原本影院系统里 “未登录不能购票” 的逻辑,直接替换成 “未登录不能召唤铠甲打怪”,贴合 “召唤人授权” 的设定,还能避免空指针崩溃(新手常犯的错);踩坑记录:一开始没加这个校验,测试时currentUser为 NULL,直接导致程序崩溃,后来加上判空和友好提示,既安全又符合剧情。

步骤 2:校验是否授权铠甲 —— 没铠甲怎么打怪?(业务闭环)

order* userOrder = orderList;
int hasArmor = 0;
while (userOrder != NULL) {
if (userOrder->userId == currentUser->id) {
hasArmor = 1;
break;
}
userOrder = userOrder->next;
}
if (hasArmor == 0) {
printf("❌ 你还没有授权任何铠甲!快去【召唤授权】模块申请刑天/飞影铠甲吧~\\n");
getchar(); getchar();
clearScreen();
return;
}

技术考点:链表遍历、条件判断、状态标记;整活思路:把影院系统 “未购票不能选座” 改成 “未授权铠甲不能打怪”,提示语也从 “请先购票” 改成 “申请刑天 / 飞影铠甲”,瞬间有内味了;优化点:找到授权记录后用break终止遍历,避免无效循环(新手常写 “遍历整个链表”,效率低)。

步骤 3:选择怪兽出没场景(整活核心,贴合铠甲勇士剧情)

printf("\\n🎮 ===== 选择讨伐场次(怪兽出没场景)=====\\n");
printf("1: 铠一-城市市区(炎龙铠甲主场)\\n2: 铠一-郊外山林(风鹰铠甲主场)\\n3: 铠一-沿海区域(黑犀铠甲主场)\\n");
printf("4: 铠一-远古遗迹(地虎铠甲主场)\\n5: 铠二-希望市(刑天铠甲主场)\\n6: 铠二-阿瑞斯星球(修罗铠甲主场)\\n7: 铠二-其他区域\\n");

技术考点:枚举类型、switch 分支、输入校验;整活思路:把影院系统的 “电影场次” 直接替换成铠甲勇士的经典场景 —— 铠一的城市 / 山林,铠二的希望市 / 阿瑞斯星球,还标注了 “主场铠甲”,让用户有 “选地图打怪” 的游戏感;细节加分:输入错误时不是单纯提示 “错误”,而是明确告知 “选择 1-7 之间的场次”,新手体验更好。

步骤 4:筛选当前场景未封印的怪兽(链表遍历 + 条件筛选)

while (tempMonster != NULL) {
if (tempMonster->isDefeated == 0 && tempMonster->scene == targetScene) {
hasUndefeated = 1;
monsterCount++;
monsterIds[monsterCount – 1] = tempMonster->id;
monsterListArr[monsterCount – 1] = tempMonster;
printf("%d. 怪兽名称:%s | 属性:%s | 危险等级:%d级\\n",
monsterCount, tempMonster->name, attrToString(tempMonster->attr), tempMonster->danger);
}
tempMonster = tempMonster->next;
}

技术考点:双层链表遍历、数组存储指针、格式化输出;整活思路:把 “电影列表” 改成 “怪兽列表”,展示怪兽名称、属性、危险等级,像游戏里的 “怪物图鉴”,还筛选 “未封印” 的怪兽,贴合 “讨伐” 的核心玩法;关键优化:用monsterListArr存储怪兽指针,避免后续选择时重复遍历链表(新手常写 “选一次遍历一次”,代码冗余)。

步骤 5:选择要讨伐的怪兽(输入校验,避免崩溃)

if (monsterChoice < 1 || monsterChoice > monsterCount) {
clearScreen();
printf("❌ 输入错误!请选择1-%d之间的序号!\\n", monsterCount);
getchar(); getchar();
clearScreen();
return;
}

技术考点:输入合法性校验、数组越界防护;踩坑记录:一开始没加这个校验,测试时输入 “0” 或 “100”,直接导致monsterListArr[monsterChoice – 1]数组越界,程序崩了好几次,加上校验后稳了;用户体验:提示语明确告知 “1-% d 之间”,比单纯说 “输入错误” 更友好。

步骤 6:选择出战铠甲(筛选闲置铠甲,核心业务规则)

while (userOrder != NULL) {
if (userOrder->userId == currentUser->id) {
movie* p = movieList;
while (p != NULL) {
if (p->id == userOrder->movieId && p->status == ARMOR_IDLE) {
armorCount++;
armorList[armorCount – 1] = p;
printf("%d. 铠甲名称:%s | 属性:%s | 剩余授权时长:%d小时\\n",
armorCount, p->title, attrToString(p->attr), userOrder->quantity);
break;
}
p = p->next;
}
}
userOrder = userOrder->next;
}

技术考点:双层链表遍历、状态筛选、模块化输出;整活思路:把 “选座位” 改成 “选出战铠甲”,只展示 “闲置” 的铠甲(战斗中 / 破损的不能选),还展示剩余授权时长,贴合 “授权使用” 的设定;效率优化:内层遍历找到铠甲后用break终止,避免无效循环(课设里这个细节能让老师眼前一亮)。

步骤 7:确认出战铠甲(输入校验,基础防护)

和步骤 5 逻辑类似,但重点是 “筛选可出战铠甲”—— 新手容易忽略 “铠甲状态”,比如让 “破损的铠甲” 也能出战,我这里加了p->status == ARMOR_IDLE的筛选,既符合剧情,又体现程序的健壮性。

步骤 8:核心战斗逻辑(属性克制 + 等级判断,整活灵魂)

if (
(battleArmor->attr == ATTR_FIRE && targetMonster->attr == ATTR_WIND) || // 火克风
(battleArmor->attr == ATTR_WIND && targetMonster->attr == ATTR_EARTH) || // 风克土
(battleArmor->attr == ATTR_EARTH && targetMonster->attr == ATTR_WATER) || // 土克水
(battleArmor->attr == ATTR_WATER && targetMonster->attr == ATTR_THUNDER) || // 水克雷
(battleArmor->attr == ATTR_THUNDER && targetMonster->attr == ATTR_FIRE) // 雷克火
) {
battleResult = 1;
printf("🔥 【属性克制】%s对%s造成致命伤害!克制效果拉满!\\n", battleArmor->title, targetMonster->name);
}

技术考点:多条件判断、三分支逻辑、字符串拼接输出;整活思路:这是整个函数的灵魂!参考铠甲勇士的五行相克设定,设计 “属性克制” 规则 —— 火克风、风克土、土克水、水克雷、雷克火,克制直接胜利,被克制直接失败,同属性看 “授权等级”(即price);趣味加分:输出语用 “致命伤害”“碾压”“压制” 等游戏化词汇,控制台像玩小游戏一样有代入感。

步骤 9:战斗结算(更新状态,剧情闭环)

if (battleResult == 1) {
targetMonster->isDefeated = 1;
battleArmor->status = ARMOR_IDLE;
printf("🎉 战斗胜利!%s已被%s封印!守护成功!\\n", targetMonster->name, battleArmor->title);
}
else {
battleArmor->status = ARMOR_DAMAGED;
printf("💥 战斗失败!%s破损严重,需要前往维修站维修!\\n", battleArmor->title);
}

技术考点:结构体属性修改、状态枚举应用;整活思路:胜利则 “封印怪兽”(isDefeated=1),失败则 “铠甲破损”(status=ARMOR_DAMAGED),贴合铠甲勇士的剧情,还为后续 “铠甲维修” 功能埋下伏笔;细节:胜利后铠甲恢复 “闲置” 状态,失败后标记为 “破损”,状态流转完整,程序逻辑闭环。

步骤 10:更新订单战斗记录(关联数据,持久化基础)

while (userOrder != NULL) {
if (userOrder->userId == currentUser->id && userOrder->movieId == battleArmor->id) {
userOrder->monsterId = targetMonster->id;
userOrder->battleResult = battleResult;
break;
}
userOrder = userOrder->next;
}

技术考点:链表精准匹配、结构体关联;整活思路:把原本的 “购票订单” 升级成 “铠甲授权 + 战斗记录”,关联讨伐的怪兽 ID 和战斗结果,后续在 “我的订单” 里能看到 “讨伐了哪个怪兽、赢了还是输了”,数据更完整;课设加分:这是 “数据关联” 的核心体现,比单纯的 “增删改查” 高出一个档次。

三、课设加分技巧:从 “能用” 到 “好用”,这些细节让老师眼前一亮

1. 模块化封装(避免代码冗余)

把attrToString()、sceneToString()、clearScreen()等重复使用的逻辑封装成独立函数,避免在主函数里写大量重复代码 —— 比如属性转换,新手常写嵌套三目运算符:

// 新手写法(冗余)
p->attr == ATTR_FIRE ? "火属性" : (p->attr == ATTR_WATER ? "水属性" : …)
// 优化写法(模块化)
attrToString(p->attr)

不仅代码更简洁,后续想改属性名称(比如加 “光属性”),只需要改attrToString()函数,不用改主逻辑,符合 “高内聚低耦合” 的编程思想。

2. 输入校验(避免程序崩溃)

整个函数里所有用户输入(场景选择、怪兽选择、铠甲选择)都加了合法性校验,比如:

if (monsterChoice < 1 || monsterChoice > monsterCount) {
clearScreen();
printf("❌ 输入错误!请选择1-%d之间的序号!\\n", monsterCount);
getchar(); getchar();
clearScreen();
return;
}

新手常犯的错就是 “相信用户输入都是合法的”,结果输入 “0” 或 “100” 导致数组越界崩溃,加了校验后,程序稳定性直接拉满。

3. 友好提示(提升交互体验)

所有提示语都不是单纯的 “错误”“成功”,而是:

  • 错误时说明原因:“你暂无闲置铠甲可出战!(原因:已出战 / 维修中 / 破损)”
  • 引导时给出方向:“快去【召唤授权】模块申请刑天 / 飞影铠甲吧~”
  • 游戏化输出:用🎉🔥💥等 emoji,用 “封印”“破损”“授权等级” 等铠甲勇士专属词汇;控制台不再是 “冷冰冰的代码界面”,而是 “有剧情的游戏界面”,这也是整活的核心 —— 让枯燥的 C 语言程序有 “灵魂”。

4. 效率优化(链表遍历终止)

所有链表遍历找到目标后,都用break终止循环,比如:

if (p->id == userOrder->movieId && p->status == ARMOR_IDLE) {
// … 业务逻辑 …
break; // 找到即终止,避免无效遍历
}

新手常写 “遍历整个链表”,哪怕找到目标了还继续遍历,效率低,这个细节能体现你对算法的理解,课设里绝对加分。

四、扩展思路:给你的铠甲勇士系统加更多玩法(卷赢全班)

写好battleMonster()函数后,还能加这些功能,让你的课设直接封神:

1. 铠甲维修功能

战斗失败后铠甲会破损,新增repairArmor()函数,让用户 “支付维修费用” 修复铠甲,修复期间铠甲状态为ARMOR_REPAIRING,无法出战 —— 既加了新功能,又完善了状态流转。

2. 怪兽刷新功能

被封印的怪兽过一段时间(比如程序重启后)自动刷新为 “未封印”,新增refreshMonster()函数,让游戏有 “重复玩” 的价值。

3. 铠甲升级功能

授权时长越长,铠甲price越高(等级越高),同属性对决时胜率更高,新增upgradeArmor()函数,让用户 “续费授权” 升级铠甲。

4. 多用户切换

支持多个召唤人登录,比如 “李昊天(刑天)”“徐霆飞(飞影)”“吴刚(金刚)”,每个用户有自己的授权铠甲和战斗记录。

5. 战斗日志

新增showBattleLog()函数,展示所有战斗记录,包括 “召唤人、铠甲、怪兽、场景、结果、时间”,像游戏里的 “战绩面板”。

五、新手避坑指南:我写这个函数时踩过的 5 个大坑

坑 1:空指针崩溃(未校验currentUser)

一开始没加登录校验,测试时currentUser为 NULL,直接导致userOrder->userId == currentUser->id崩溃 —— 解决方案:所有涉及用户的操作前,先判空。

坑 2:数组越界(未校验输入范围)

选择怪兽时输入 “0”,导致monsterListArr[monsterChoice – 1]访问monsterListArr[-1],程序崩了 —— 解决方案:所有用户输入都加 “最小值 + 最大值” 校验。

坑 3:重复遍历链表(效率低)

一开始选择怪兽后,又遍历一次链表找怪兽,代码冗余且效率低 —— 解决方案:用数组存储怪兽指针,一次遍历,多次使用。

坑 4:属性转换乱码(未用 static 变量)

attrToString()函数一开始没加static,返回局部变量地址,导致输出乱码 —— 解决方案:辅助函数返回字符串时,用static变量存储,避免局部变量销毁。

坑 5:清屏函数不兼容(Windows/Linux)

一开始用system("cls"),在 Linux 系统上运行报错 —— 解决方案:用条件编译适配 Windows/Linux:

#ifdef _WIN32
system("cls");
#else
system("clear");
#endif

六、总结:为什么我的课设能从 “枯燥” 到 “好玩”?

其实核心就两点:

  • 保留考点:不管怎么整活,battleMonster()函数始终覆盖了 C 语言课设的核心考点 —— 链表、结构体、枚举、条件判断、循环、文件操作、模块化编程,老师要的 “知识点” 一个没少;
  • 加入趣味:把影院系统的逻辑替换成铠甲勇士的世界观,加属性克制、场景讨伐、战斗结算,让代码有 “剧情”,有 “游戏感”,不再是单纯的 “增删改查”。
  • 写这篇博客的时候,我翻了当初的课设代码,从一开始的 “应付式写影院系统”,到后来越写越上头,给每个函数加铠甲勇士的设定,其实编程的乐趣就在于此 —— 把自己喜欢的东西融入代码,让枯燥的语法变成有趣的作品。

    如果你也是被 C 语言课设逼到挠头的大一新生,不妨试试我的思路:把课设和自己喜欢的 IP 结合,哪怕只是改改变量名、加加提示语,也能让你的代码与众不同。最后放一句我写这个函数时的感悟:编程不是重复敲代码,而是用代码创造属于自己的世界。

    附:新手快速上手指南

  • 复制完整代码到 Dev-C++/VS/CLion;
  • 运行main()函数,自动初始化测试数据(李昊天 + 刑天铠甲 + 路法怪兽);
  • 按照控制台提示选择场景(选 5:希望市)→ 选择怪兽(选 1:路法)→ 选择铠甲(选 1:刑天铠甲);
  • 体验 “火属性刑天铠甲克制风属性路法” 的胜利效果;
  • 尝试修改铠甲 / 怪兽属性,体验不同的战斗结果。
  • 赞(0)
    未经允许不得转载:171主机测评 » 整活天花板!我给 C 语言课设加了「铠甲勇士打怪系统」,属性克制 + 场景讨伐,玩到停不下来
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址