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

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

  1. 炉石传说 C# 开发笔记

    最近在大连的同事强力推荐我玩 炉石传说,一个卡牌游戏.加上五一放一个很长很长的假期,为了磨练自己,决定尝试开发一个C#的炉石传说. 这件事情有人已经干过了,开发了一个网页版的炉石,但是貌似不能玩... ...

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

    在移动网页,尤其是webapp中常需要用到大量的css3动画,来获得良好交互体验 我之前帮朋友做了一个,可惜没帮上忙现在和大家分享一下 目标是要做一个<炉石传说>游戏的介绍宣传页面,文字内 ...

  3. 炉石传说 C# 开发笔记(6月底小结)

    炉石传说的开发,已经有30个工作日了. 关于法术的定义方法,有过一次重大的变更:法术效果是整个炉石的核心,正是因为丰富的法术效果,才造就了炉石的可玩性. 原来构思的时候,对于法术效果没有充分的理解,所 ...

  4. 炉石传说 C# 开发笔记 (续)

    炉石传说山寨的工作一直在进行着,在开发过程中深深体会到,对于业务的理解和整个程序的架构的整理远比开发难得多. 在开发过程中,如果你的模型不合理,不准确,很有可能造成代码的混乱,冗余,难以维护和扩展性比 ...

  5. 炉石传说__multiset

     炉石传说  Problem Description GG学长虽然并不打炉石传说,但是由于题面需要他便学会了打炉石传说.但是传统的炉石传说对于刚入门的GG学长来说有点复杂,所以他决定自己开发一个简化版 ...

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

    上一篇文章我们看到了<炉石传说>核心存储卡的数据,今天,我们不断探索卡&身手. 基本的类 通过之前的分析,卡牌&技能涉及到几个类体系:Entity.Actor.Card.S ...

  7. fzu Problem - 2232 炉石传说(二分匹配)

    题目链接:http://acm.fzu.edu.cn/problem.php?pid=2232 Description GG学长虽然并不打炉石传说,但是由于题面需要他便学会了打炉石传说.但是传统的炉石 ...

  8. islands打炉石传说<DP>

    islands最近在完一款游戏"炉石传说",又名"魔兽英雄传".炉石传说是一款卡牌类对战的游戏.游戏是2人对战,总的来说,里面的卡牌分成2类,一类是法术牌,另一 ...

  9. CCF2016093炉石传说(C语言版)

    问题描述 <炉石传说:魔兽英雄传>(Hearthstone: Heroes of Warcraft,简称炉石传说)是暴雪娱乐开发的一款集换式卡牌游戏(如下图所示).游戏在一个战斗棋盘上进行 ...

随机推荐

  1. .net开发过程中遇到的错误,及解决办法记录

    一.在证书存储区中找不到清单签名证书. 解决方法:右击项目属性—>签名—>为ClickOnce清单签名,将勾掉的选项去掉. 参考:http://www.cnblogs.com/190196 ...

  2. Javascript之旅——第九站:吐槽function

    说到funciton,也是我对js非常吐槽的一点,封装的让我眼瞎,马蛋的,哥只能大眼睁着去黑盒的使用,简直只有完完全全的听各类图书对 function的道听图说,完全没有做到一点点的眼见为实. 一:f ...

  3. 说完Pivot 今天说下Unpivot 的处理方式

    上次说到,既然有Pivot 的行转列,那么肯定也有Unpivot 的列转行 .其实unpivot 处理的情况也是差不多,也是分3步走. 首先也是先演示一下unpivot 的用法 ),Mon TIME, ...

  4. Linux rm 删除文件

    rm 删除文件rm -f 强制删除-i 提示-r 删除目录的时候-v 可实话 rm -rfv 多目录 不提示 [root@wang whp]# touch .txt [root@wang whp]# ...

  5. JMS学习(六)-ActiveMQ的高可用性实现

    原文地址:http://www.cnblogs.com/hapjin/p/5663024.html 一,ActiveMQ高可用性的架构 ActiveMQ的高可用性架构是基于Master/Slave 模 ...

  6. android代码优化----ListView中自定义adapter的封装(ListView的模板写法)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  7. Android开发中上线后修改应用名称的若干问题

    一.在Android Studio 1.3中修改app的包名: 需求来源: 之前开发的app已经在腾讯的应用宝上线,应客户要求,app需要改名字,这个就有点麻烦了.如果申请改名字,要求如下: 截图上图 ...

  8. CSS样式----图文详解(二):css属性

    主要内容 CSS的单位 字体属性 文本属性 背景属性 列表属性 盒子模型 定位属性:position.float.overflow.z-index等 导航栏的制作 鼠标的属性cursor 滤镜的介绍 ...

  9. 第66课 C++中的类型识别

    1. 类型识别 (1)在面向对象中可能出现下面的情况 ①基类指针指向子类对象 ②基类引用成为子类对象的别名 ▲静态类型——变量(对象)自身的类型(定义变量类型时类型或参数类型) ▲动态类型——指针(引 ...

  10. Carcraft

    魔兽登录系统   创建魔兽系统相关窗体: 登录窗体(frmLogin) 注册窗体(frmRegister) 主窗体   (frmMain) 实现魔兽登录系统: 登录的界面如下 实现思路: 1.创建一个 ...