从炉石传说的一个自杀OTK说起

OTK就是one turn kill,不过这次我们要谈的OTK是自杀,对就是自己把自己给OTK了。

其实程序没有任何错误,只是恰巧碰上了这么个死循环。

ps:文章最后有代码git地址

发动条件及效果:

奥金尼(奥金尼特效是你的回复生命的牌和技能改为造成等量伤害)

痛苦女王(每当该随从造成伤害,为你的英雄回复等量生命值)

奥金尼在场的时候当痛苦女王发动攻击的时候,进行痛苦女王的特效判定,回复生命1,又触发奥金尼的特效,扣自己1点血,再次触发痛苦女王造成伤害的特效,再次回复生命1,又触发奥金尼特效,扣自己1点血,然后造成死循环,直到自己挂掉为止。

有兴趣的童鞋可以看下面这个视频:http://h.163.com/15/0313/16/AKJOMG4O00314RES.html

当然视频里的猜测是肯定不对的,按照程序员的思路来看应该是进入死循环了。

程序模拟:

我们希望能够模拟一下这个场景,并且尝试去实现一下炉石的战斗逻辑,下面的图就是我们的模拟效果,大致实现了一些卡牌和功能。

基础类设计

首先是baseUnit,这个类实现大部分基础的内容,在这个类基础上衍生了卡片和英雄,然后卡片在衍生出魔法卡片,武器卡片,随从卡片等。

基础类里包含了血量、护甲、圣盾、攻击次数等属性,可以实现一些炉石中的基础攻击,还没有考虑aoe技能的实现和恐狼这样的光环设计。

基本的风怒,actCount可以+1;冲锋,战吼的时候actCount+1,默认的随从上场actCount是0;

不过这些都不是重点,我们的重点是在攻击后的判定如何实现,这里baseUnit里也没有实现随从对拼的逻辑,死亡逻辑,只是搭了个架子。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HeartStone.Interface;

namespace HeartStone.BaseDomain
{
	public class BaseUnit : IUnitAction
	{
		public BaseUnit(BaseHero pHero)
		{
			OwnerHero = pHero;
			Name = "未知";
			Ac = 0;
			ActCount = 0;
			Ad = 0;
			BaseAd = 0;
			BaseHP = 0;
			this.HeroType = HeroType.None;
			this.Hp = 0;
			this.Shield = false;

		}

		public virtual void Dispose()
		{
			Console.WriteLine("{0}死亡", this.Name);
			return;
		}

		public BaseHero OwnerHero { get; set; }
		/// <summary>
		/// 职业类型
		/// </summary>
		public HeroType HeroType { get; set; }

		public string Name { get; set; }

		private int _Hp;
		/// <summary>
		/// 血量
		/// </summary>
		public int Hp
		{
			get { return _Hp; }
			set
			{
				//血量最高不得超过基础血量
				if (value > BaseHP)
					_Hp = BaseHP;
				else
					_Hp = value;
			}
		}
		/// <summary>
		/// 基础血量
		/// </summary>
		public int BaseHP { get; set; }

		private int _Ac { get; set; }
		/// <summary>
		/// 护甲
		/// </summary>
		public int Ac
		{
			get { return _Ac; }
			set
			{
				//护甲最多不能超过基础血量
				if (value > BaseHP)
					_Ac = BaseHP;
				else
					_Ac = value;
			}
		}

		/// <summary>
		/// 攻击
		/// </summary>
		public int Ad { get; set; }

		/// <summary>
		/// 基本攻击
		/// </summary>
		public int BaseAd { get; set; }

		/// <summary>
		/// 攻击次数,冲锋上场+1,风怒每回合+1
		/// </summary>
		public int ActCount { get; set; }

		private bool _Shield = false;

		/// <summary>
		/// 圣盾
		/// </summary>
		public bool Shield
		{
			get { return _Shield; }
			set { _Shield = value; }
		}

		public virtual void Attack(BaseUnit targeted, BaseUnit from, int ad)
		{
			targeted.Defense(targeted, from, ad);
		}

		public virtual void Defense(BaseUnit targeted, BaseUnit from, int ad)
		{
			if (Shield)
				Shield = false;
			else
			{
				int acDefend = 0;
				if (targeted.Ac > 0)
				{
					acDefend = targeted.Ac - ad;

					if (acDefend >= 0)
						targeted.Ac -= ad;	//攻击5,护甲10,10-5大于等于0,表示护甲比攻击高,只要减护甲即可
					else
					{
						//攻击5,护甲2,2-5=-3,小于0,表示护甲不够,要减
						targeted.Ac = 0;
						targeted.Hp += acDefend;
					}
				}
				else
					targeted.Hp -= ad;
				Console.WriteLine("{0}受到{1}的{2}伤害,当前护甲{3},生命{4}", targeted.Name, from.Name, ad, targeted.Ac, targeted.Hp);
				from.MakeDamage(targeted, from, ad);
			}
		}

		/// <summary>
		/// 执行治疗
		/// </summary>
		/// <param name="targeted"></param>
		/// <param name="from"></param>
		/// <param name="ad"></param>
		public void Treat(BaseUnit targeted, BaseUnit from, int ad)
		{
			var hero = from as BaseHero;
			bool flag = true;
			if (hero != null)
				flag = hero.treatFlag;
			else
				flag = from.OwnerHero.treatFlag;

			if (flag)
			{
				targeted.Hp += ad;
				Console.WriteLine("{0}受到{1}的{2}治疗", targeted.Name, from.Name, ad);
			}
			else
			{
				if (targeted.Hp >= 0)
				{
					//from制造了伤害
					Defense(targeted, from, ad);
				}
			}
		}

		public virtual void MakeDamage(BaseUnit targeted, BaseUnit from, int ad)
		{
			return;
		}
	}
}

  

奥金尼的实现

奥金尼的效果是你的治疗变成等量的伤害,所以对手的治疗依然是治疗,所以我给英雄增加了一个属性,treatFlag,用于标记这个英雄的治疗状态。

那么卡片自带的治疗效果,譬如大地之环或者老司机呢,所有卡片在上场的时候,也就是new的时候必须要传递一个owner,来表示这张卡是属于谁的,那么就可以很容易的找到这个英雄的治疗状态了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HeartStone.BaseDomain;

namespace HeartStone.Units.UnitCard
{
	public class OKinny : BaseCard
	{
		public OKinny(BaseHero pHero)
			: base(pHero)
		{
			Crystal = 4;
			BaseCrystal = 4;
			Ad = 3;
			BaseAd = 3; BaseHP = 5;
			Hp = 5;

			Name = "奥金尼";
			//牧师职业卡
			HeroType = HeroType.Minister;
			//奥金尼战场效果
			pHero.treatFlag = false;
			Console.WriteLine("{0}召唤登场",Name);
		}

		~OKinny()
		{

			this.OwnerHero.treatFlag = true;
		}

		public override void Dispose()
		{
			//奥金尼死了要重置
			this.OwnerHero.treatFlag = true;
			base.Dispose();
		}

	}
}

  

痛苦女王的实现

痛苦女王的效果是,造成伤害时回复等量的生命,所以痛苦女王需要重写MakeDamage方法,然后给自己英雄回复等量生命。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HeartStone.BaseDomain;

namespace HeartStone.Units.UnitCard
{
	public class QueenPain : BaseCard
	{
		public QueenPain(BaseHero pHero)
			: base(pHero)
		{
			Crystal = 2;
			BaseCrystal = 2;
			Ad = 1;
			BaseAd = 1; BaseHP = 4;
			Hp = 4;
			Name = "痛苦女王";
			//牧师职业卡
			HeroType = HeroType.Minister;
			Console.WriteLine("{0}召唤登场", Name);
		}

		public override void MakeDamage(BaseUnit targeted, BaseUnit from, int ad)
		{
			Treat(from.OwnerHero, this, ad);
			base.MakeDamage(targeted, from, ad);
		}
	}
}

  

治疗的实现

治疗的实现时需要判断treatFlag,如果treatFlag为false的话就变成造成等量的伤害,然后再次调用from.MakeDamage方法,这样就可以循环起痛苦女王制造伤害的效果了,当然为了防止真的死循环,这里需要对目标的生命值进行一个判断,如果目标生命值小于0了则停止继续循环。

/// <summary>
		/// 执行治疗
		/// </summary>
		/// <param name="targeted"></param>
		/// <param name="from"></param>
		/// <param name="ad"></param>
		public void Treat(BaseUnit targeted, BaseUnit from, int ad)
		{
			var hero = from as BaseHero;
			bool flag = true;
			if (hero != null)
				flag = hero.treatFlag;
			else
				flag = from.OwnerHero.treatFlag;

			if (flag)
			{
				targeted.Hp += ad;
				Console.WriteLine("{0}受到{1}的{2}治疗", targeted.Name, from.Name, ad);
			}
			else
			{
				if (targeted.Hp >= 0)
				{
					//from制造了伤害
					Defense(targeted, from, ad);
				}
			}
		}

  

英雄和技能的实现

英雄的技能考虑到战士是瞬发的,法师可以选择目标,当然还会有可以只能选自己随从的技能或者只能选英雄的技能,等等。 所以在这里我们加了一个taregtObject,用于标记这个技能可以指向谁。

然后就是实现每个英雄的代码了,我实现了3个英雄,这里展示一下牧师的实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HeartStone.BaseDomain;

namespace HeartStone.Units.Hero
{
	public class HeroMinister : BaseDomain.BaseHero
	{
		/// <summary>
		/// 构造函数
		/// </summary>
		public HeroMinister()
			: base()
		{
			Name = "牧师";
			//初始化自己的技能
			HeroSkill = new MinisterSkill(this);
		}
	}

	public class MinisterSkill : BaseHeroSkill
	{

		public MinisterSkill(BaseHero pHero)
			: base(pHero)
		{
			TargetObject = TargetedObject.AllUnit;
		}

		/// <summary>
		/// 牧师的技能
		/// </summary>
		public override void Skill(BaseUnit taregted)
		{
			Console.WriteLine("{0}使用了技能,目标{1}", thisHero.Name, taregted.Name);
			this.thisHero.Treat(taregted, thisHero, 2);

		}
	}
}

  

模拟效果

然后我们就可以进行一个简单的模拟了,不考虑水晶,直接模拟环境

static void Main(string[] args)
		{
			HeroMinister minister = new HeroMinister();
			HeroWarrior warrior = new HeroWarrior();
			//战士回合,战士使用技能,结束
			warrior.HeroSkill.Skill(warrior);
			Console.WriteLine("================回合结束================");

			//牧师回合,牧师上奥金尼,痛苦女王,用治疗伤害战士
			OKinny oKinny = new OKinny(minister);
			QueenPain queenPain = new QueenPain(minister);
			minister.HeroSkill.Skill(warrior);
			Console.WriteLine("================回合结束================");

			//战士回合,战士使用技能,结束
			warrior.HeroSkill.Skill(warrior);
			Console.WriteLine("================回合结束================");

			//牧师回合,奥金尼攻击,痛苦女王攻击,牧师卒
			oKinny.Attack(warrior, oKinny, oKinny.Ad);
			queenPain.Attack(warrior, queenPain, queenPain.Ad);
			Console.WriteLine("================回合结束================");
			Console.ReadKey();
		}

  

写在最后

平时玩的时候觉得炉石挺简单的一个游戏,模拟一下这个场景应该很容易,但真正去实现的时候发现还是有很多地方缺乏考虑,经过一天的修改才最终出来这个模拟的雏形,还有很多对战逻辑没有实现,譬如aoe的法术还没想到什么好的方法来进行实现。

Git:https://git.oschina.net/jangojing/HeartStoneTest.git

时间: 2024-11-06 09:15:56

从炉石传说的一个自杀OTK说起的相关文章

炉石传说 C# 开发笔记

最近在大连的同事强力推荐我玩 炉石传说,一个卡牌游戏.加上五一放一个很长很长的假期,为了磨练自己,决定尝试开发一个C#的炉石传说. 这件事情有人已经干过了,开发了一个网页版的炉石,但是貌似不能玩.... http://cnodejs.org/topic/529c1366a6957a0809485f3d 如果这位同志看到这篇文章,请一定和我联系!! [email protected]或Q我377372779 第一天 开始学习炉石传说的玩法,最好的方法是不停的玩游戏. 一个应用是否逻辑清晰,取决于你

[原创]webapp/css3实战,制作一个《炉石传说》宣传页

在移动网页,尤其是webapp中常需要用到大量的css3动画,来获得良好交互体验 我之前帮朋友做了一个,可惜没帮上忙现在和大家分享一下 目标是要做一个<炉石传说>游戏的介绍宣传页面,文字内容不多,主要是要和用户有互动 脑海中闪过很多见过的css3效果,决定总体采用一个3d翻转效果,每页再堆砌一些小动画 效果/演示 (在支持css3的浏览器中才能显示,ie11目前都不支持3d转换) div{position:absolute;top:0px;left:0px; overflow:hidden;}

炉石传说JJC英雄简评 &amp;&amp; JJC combo收集:Ver 1.0

? 1 2 3 4 5 6 7 /*-----------------------------------------------------------------------------  *  author:Rainboy and his GL 南柯CHY  *  wirte date:2014-04-29  * # LastUpdated: 2014-04-29 15:42  * ? Copyright 2014 Rainboy and his GL 南柯CHY.  All Rights

CCF - 201609-3 - 炉石传说

问题描述 试题编号: 201609-3 试题名称: 炉石传说 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 <炉石传说:魔兽英雄传>(Hearthstone: Heroes of Warcraft,简称炉石传说)是暴雪娱乐开发的一款集换式卡牌游戏(如下图所示).游戏在一个战斗棋盘上进行,由两名玩家轮流进行操作,本题所使用的炉石传说游戏的简化规则如下: * 玩家会控制一些角色,每个角色有自己的生命值和攻击力.当生命值小于等于 0 时,该角色死亡.角色分为英雄和随从. *

CCF2016093炉石传说(C语言版)

问题描述 <炉石传说:魔兽英雄传>(Hearthstone: Heroes of Warcraft,简称炉石传说)是暴雪娱乐开发的一款集换式卡牌游戏(如下图所示).游戏在一个战斗棋盘上进行,由两名玩家轮流进行操作,本题所使用的炉石传说游戏的简化规则如下: * 玩家会控制一些角色,每个角色有自己的生命值和攻击力.当生命值小于等于 0 时,该角色死亡.角色分为英雄和随从. * 玩家各控制一个英雄,游戏开始时,英雄的生命值为 30,攻击力为 0.当英雄死亡时,游戏结束,英雄未死亡的一方获胜. * 玩

《炉石传说》建筑设计欣赏(6):卡&amp;amp;在执行数据时,组织能力

上一篇文章我们看到了<炉石传说>核心存储卡的数据,今天,我们不断探索卡&身手. 基本的类 通过之前的分析,卡牌&技能涉及到几个类体系:Entity.Actor.Card.Spell,令人十分困惑,特别是前两者.在这里先略带武断的说一下这几个类的基本定位: Entity主要用来做网络数据同步用的: Actor主要处理client的渲染对象的控制,作为Component挂载在资源对象上: Spell是技能Prefab挂载的脚本. Card是卡牌Prefab挂载的脚本.在执行时处于中

《炉石传说》架构设计赏析(6):卡牌&amp;技能数据的运行时组织

前一篇文章我们看到了<炉石传说>的核心卡牌数据的存储,今天我们继续探索卡牌&技能. 主要的类 通过之前的分析,卡牌&技能涉及到几个类体系:Entity,Actor,Card,Spell,令人十分困惑,特别是前两者.在这里先略带武断的说一下这几个类的基本定位: Entity主要用来做网络数据同步用的: Actor主要处理客户端的渲染对象的控制,作为Component挂载在资源对象上: Spell是技能Prefab挂载的脚本: Card是卡牌Prefab挂载的脚本,在运行时处于中心

《炉石传说》架构设计赏析(4):Asset管理

欢迎转载,请注明作者[燕良@游戏开发]及原文地址:http://blog.csdn.net/neil3d/article/details/39580197 另外,欢迎大家来我的QQ群交流各种游戏引擎相关的技术:游戏引擎能吃吗(264656505) 话说,经过这段时间的学习和摸索,对于Unity3D的开发思路已经基本清晰了.唯独还剩下一个AssetBundle机制还没有搞透,这个涉及到前期项目的资源规划.资源管理代码的写法,以及自动更新机制的实现. 所以,还是想先把游戏逻辑的进一步分析押后,先来看

关于炉石传说AI

一直想做一个炉石传说自动出牌AI,感谢yangyuan在GITHUB上分享的hearthrock(https://github.com/yangyuan/hearthrock),让我不至于毫无头绪.目前,hearthrock(近两年前的项目,作者已不更新)的ai部分代码已经基本读完,该AI智能程度较低,基本就是有什么牌出什么牌,基本达不到要求.苦思冥想之余下载了德国人制作的Hearthbuddy,意外发现程序文件夹中带有AI功能源码且是C#的!量很大,得慢慢吃.