# HW2: RPG Game FAQ
## Output Format
1. 在*S2*: select targets時,印出的candidates索引中會跳過死掉的units嗎?
已死的units並不會被包含在candidates中,而給定N個candidates,索引則從0算到n-1。以下為例:
**Given**: 4個Enemy units [A, B, C, D],其中B, D死亡。
**When**: 你使用Waterball(攻擊對象:一位)
**Then**: 輸出以下
`Select 1 targets: (0) [2]A (1) [2]C`
## AI
1. 如果AI的`selectActions`方法,回傳了不合法的action (e.g.,魔力不足以執行該action),該怎麼辦?
你**只能傳合法Actions的索引**給他做選擇。
例如:如果AI具有四個Actions,其索引為 `[0,1,2,3]`,並且AI當下的魔力只足夠於使用0或3,則你必須傳入`[0,3]`進`selectActions` 方法中。在此例中AI則會回傳0或3其中一個索引作為他的選擇。
2. 根據 spec 上寫,"The assumption of these AI units is that the selectAction and selectTarget methods are called only once in the unit's turn.",是否在以下情形也要 call selectTarget?
(a) AI 使用全體技(如:Fireball)、AI 使用沒有目標的技能(如:Summon)、AI 使用只能以自己為目標的技能(如:SelfHealing)
(b) AI 使用 Cheerup 要選擇 3 個目標,但場上只剩下 2 個合法目標,此時是否仍要 call selectTarget(2,2) ?
這些情況皆不需要呼叫`selectTarget`。
只有要求specific targets的技能和場合才需要呼叫`selectTarget`唷。
> 規格在S2中有提到說在(b)提及的場合下RPG會自動選擇targets。
## OnePunch
1. 為了使用`OnePunch`,我該如何取得`OnePunch`造成的damage?
**你無法直接地取得其damage。**
`OnePunch`具有兩個方法,其中你可以透過`int getMpCost()`來獲得此技能所需的魔力消耗。而另一個則為:
```java=
void perform(Target target)
```
其中`Target`的Interface定義如下:
```java=
public interface Target {
void takeOnePunchDamage(int damage);
}
```
你不能直接取得`OnePunch`的damage值,你必須依照他方法所示,傳一個`Target`實體進去其`perform`方法,而`OnePunch`則會在執行`perform`時,把damage再傳入`Target`實作的`takeOnePunchDamage`方法中。
2. 如果讓`Unit`類別實作`OnePunch`所需的`Target`介面,是不是就不符合**OCP**了呀?
沒錯,因為這樣一來,當你要把`OnePunch`這個技能刪除時,你也須要將`Unit`實作`Target`的部分也一併刪除。
Tips: 先當作規格裡沒有`OnePunch`來設計你的程式,等其他九個技能都實作完之後,再來想`OnePunch`的設計,可能你就會突然想到一些辦法來處理此「**外部依賴造成的外力**」。
## Self-Explosion
Q: 請問C會不會在ASelfExplosion 的途中(B die 的下一行)補血 .
也就是C會不會因為curse了B, 而在A 的 SelfExplosion 中活下來。
還是SelfExplosion結算完才會處理curse的回血。
A:
Curse目標的Unit(B)死亡的當下就會啟動Curse的效果(A回血)。
而SelfExplosion的自殺效果(turn A's HP to zero)是他造成所有Unit傷害之後才奏效的。
如以下Out所示,(Curse奏效的時機點已用"Curse Effect..."印出)
```
[1]Hero's turn (HP: 500, MP: 500, STR: 100, State: Normal).
Select an action: (0) Basic Attack (1) SelfExplosion (2) Curse
[1]Hero uses Curse on [2]Slime1.
[2]Slime1's turn (HP: 100, MP: 10000, STR: 50, State: Normal).
Select an action: (0) Basic Attack
[2]Slime1 attacks [1]Hero.
[2]Slime1 causes 50 damage to [1]Hero.
[1]Hero's turn (HP: 450, MP: 400, STR: 100, State: Normal).
Select an action: (0) Basic Attack (1) SelfExplosion (2) Curse
[1]Hero uses SelfExplosion on [2]Slime1.
[1]Hero causes 150 damage to [2]Slime1.
[2]Slime1 dies.
Curse Effect (Hero's HP + 10000)
[1]Hero dies.
You lose.
```
## Curse
Q: 如果A curse B之後,A先死了,之後B死的時候A會回血嗎?
A: 不會唷,死掉之後Curse就不會發揮作用了。(規格尚未講得十分清楚,這部分有其他疑慮請各位同學直接以測資為主,測資中的corner cases並不多。)
## Cheerup
施展Cheerup會對三名Allies,進入Cheerup狀態。Cheerup狀態下的Unit其在施放能造成傷害的Action時(Basic Attack, Waterball, Fireball, SelfExplosion, OnePunch),此Action傷害皆會加50。
[Given]
T1 = [ Hero, A, B ]
[When]
Hero對A, B施展了Cheerup
[Then]
在Cheerup狀態下(三回合內)
若A或B施展了Fireball (或是其他有傷害性的Actions)
那就會對所有enemy造成 50+50 = 100 的傷害
## OCP
1. 如果讓`Unit`類別實作`OnePunch`所需的`Target`介面,是不是就不符合**OCP**了呀?
沒錯,詳細請往上找尋`OnePunch`小節。
2. enum是不是本身就不具有OCP?
沒錯,因為新增新的type時,你必須改變舊有的enum內部程式,因此不符合OCP。enum只是一個常數空間而已,當你真的需要OCP時,就該往多型(polymorphism)的方向做思考,以及踏踏實實地善用設計重構三步驟(詳細請看規格中的TA Tips小節)。
3. OCP的評分標準:
- [What are not OCPs](https://github.com/Fundamental-OOP/HW2-RPG-BATTLE/tree/main/src/tutorials/ocp/whatarenotocp)
- [OCP Clarification](https://hackmd.io/rFlV4kBGRROFkyI1yuo6JA)
## 該怎麼寫...
1. 這一題怎麼難度又翻倍了??該怎麼下手比較好??
我的建議是,**把它放在作業Priority Queue的Top,因為這一題可能比想像中還要多花一點時間。** 而早一點開始接觸這個作業,你才能較早享受到「潛意識思考」的幫助 :),慢慢打好這一場持久戰。
最好的做法是用兩到三天的時間理解規格並把理解**專心且優雅地**畫在類別圖中,然後試著照圖實作,趕緊找到自身盲點去修正類別圖。而其中如果規格有一些地方無法釐清請直接上NTU Cool發問吧 :)!
2. 好煩躁啊啊啊
如我一開始所說,軟體工程解決的並不是「困難」的事,而是「複雜」的事。RPG中的每一個技能都不需要一個特別聰明高效率的算法,但每一個技能都會帶來新的**設計外力(Force)**。
厲害的軟體設計師,會看得出來這個外力的形狀,並能把軟體依照外力的形狀做開發。讓外力優雅地活在我們開發的軟體之中。
在我們熟悉這個技能之前,會很容易被這些外力戳得心疲力盡,但這是必須的痛楚。因此要寫好這個作業,打的是一個持久的心態戰爭。你很有可能會每多支援一個新的技能,就必須重構一部分你的程式,而每一次重構你都必須要停下腳步,意識到該外力的長相為何,為什麼你之前沒有考慮到這一點?
而使用UML做Modeling,不斷同步實作和設計圖,只是維持良好設計視角和心態的一個手段。其圖上呈現的形式(你畫的東西),也許並不是這麼重要,那只是和腦袋的一個長期的相處過程。