4 * License : The MIT License
5 * Copyright(c) 2008 olyutorskii
8 package jp.sfjp.jindolf.data;
10 import java.awt.image.BufferedImage;
11 import java.text.MessageFormat;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.LinkedList;
15 import java.util.List;
17 import jp.sfjp.jindolf.util.GUIUtils;
18 import jp.sourceforge.jindolf.corelib.LandDef;
19 import jp.sourceforge.jindolf.corelib.VillageState;
26 private static final int GID_MIN = 3;
29 private final Land parentLand;
30 private final String villageID;
31 private final int villageIDNum;
32 private final String villageName;
34 private final boolean isValid;
36 private int limitMonth;
38 private int limitHour;
39 private int limitMinute;
41 private VillageState state = VillageState.UNKNOWN;
43 private final LinkedList<Period> periodList = new LinkedList<>();
44 private final List<Period> unmodList =
45 Collections.unmodifiableList(this.periodList);
47 private final Map<String, Avatar> avatarMap =
50 private final Map<Avatar, BufferedImage> faceImageMap =
52 private final Map<Avatar, BufferedImage> bodyImageMap =
54 private final Map<Avatar, BufferedImage> faceMonoImageMap =
56 private final Map<Avatar, BufferedImage> bodyMonoImageMap =
63 * @param parentLand Villageの所属する国
64 * @param villageID 村のID
65 * @param villageName 村の名前
67 public Village(Land parentLand, String villageID, String villageName) {
68 this.parentLand = parentLand;
69 this.villageID = villageID.intern();
70 this.villageIDNum = Integer.parseInt(this.villageID);
71 this.villageName = villageName.intern();
73 this.isValid = this.parentLand.getLandDef()
74 .isValidVillageId(this.villageIDNum);
83 * @return 村の所属する国(Land)
85 public Land getParentLand(){
86 return this.parentLand;
94 public String getVillageID(){
95 return this.villageID;
103 public int getVillageIDNum(){
104 return this.villageIDNum;
112 public String getVillageName(){
113 StringBuilder name = new StringBuilder();
115 LandDef landDef = this.parentLand.getLandDef();
116 String prefix = landDef.getLandPrefix();
119 StringBuilder id = new StringBuilder(this.villageID);
120 if(landDef.getLandId().equals("wolfg")){
121 while(id.length() < GID_MIN){
127 String result = name.toString();
136 public String getVillageFullName(){
137 return this.villageName;
145 public VillageState getState(){
154 public void setState(VillageState state){
160 * 日程及び更新時刻を持っているか判定する。
162 * @return 日程が不明ならtrue
164 public boolean hasSchedule(){
165 boolean result = ! this.periodList.isEmpty();
174 public Period getPrologue(){
175 for(Period period : this.periodList){
176 if(period.isPrologue()) return period;
186 public Period getEpilogue(){
187 for(Period period : this.periodList){
188 if(period.isEpilogue()) return period;
199 public Period getProgress(int day){
200 for(Period period : this.periodList){
201 if( period.isProgress()
202 && period.getDay() == day ) return period;
208 * PROGRESS状態のPeriodの総数を返す。
210 * @return PROGRESS状態のPeriod総数
212 public int getProgressDays(){
214 for(Period period : this.periodList){
215 if(period.isProgress()) result++;
221 * 指定されたPeriodインデックスのPeriodを返す。
223 * <p>プロローグやエピローグへのアクセスも可能。
225 * @param day Periodインデックス
228 public Period getPeriod(int day){
229 return this.periodList.get(day);
233 * 指定されたアンカーの対象のPeriodを返す。
238 public Period getPeriod(Anchor anchor){
241 if(anchor.isEpilogueDay()){
242 anchorPeriod = getEpilogue();
246 int anchorDay = anchor.getDay();
247 anchorPeriod = getPeriod(anchorDay);
257 public int getPeriodSize(){
258 return this.periodList.size();
264 * @return Periodのリスト。
266 public List<Period> getPeriodList(){
267 return this.unmodList;
271 * 指定した名前で村に登録されているAvatarを返す。
273 * @param fullName Avatarの名前
276 public Avatar getAvatar(String fullName){
277 // TODO CharSequenceにできない?
280 avatar = Avatar.getPredefinedAvatar(fullName);
281 if( avatar != null ){
282 preloadAvatarFace(avatar);
286 avatar = this.avatarMap.get(fullName);
287 if( avatar != null ){
288 preloadAvatarFace(avatar);
296 * Avatarの顔画像を事前にロードする。
298 * @param avatar Avatar
300 private void preloadAvatarFace(Avatar avatar){
301 if(this.faceImageMap.get(avatar) != null) return;
303 Land land = getParentLand();
304 LandDef landDef = land.getLandDef();
306 String template = landDef.getFaceURITemplate();
307 int serialNo = avatar.getIdNum();
308 String uri = MessageFormat.format(template, serialNo);
310 BufferedImage image = land.downloadImage(uri);
311 if(image == null) image = GUIUtils.getNoImage();
313 this.faceImageMap.put(avatar, image);
321 * @param avatar Avatar
323 // 未知のAvatar出現時の処理が不完全
324 public void addAvatar(Avatar avatar){
325 if(avatar == null) return;
326 String fullName = avatar.getFullName();
327 this.avatarMap.put(fullName, avatar);
329 preloadAvatarFace(avatar);
335 * 村に登録されたAvatarの顔イメージを返す。
337 * @param avatar Avatar
340 // TODO 失敗したらプロローグを強制読み込みして再トライしたい
341 public BufferedImage getAvatarFaceImage(Avatar avatar){
342 return this.faceImageMap.get(avatar);
346 * 村に登録されたAvatarの全身像イメージを返す。
348 * @param avatar Avatar
351 public BufferedImage getAvatarBodyImage(Avatar avatar){
352 BufferedImage result;
353 result = this.bodyImageMap.get(avatar);
354 if(result != null) return result;
356 Land land = getParentLand();
357 LandDef landDef = land.getLandDef();
359 String template = landDef.getBodyURITemplate();
360 int serialNo = avatar.getIdNum();
361 String uri = MessageFormat.format(template, serialNo);
363 result = land.downloadImage(uri);
364 if(result == null) result = GUIUtils.getNoImage();
366 this.bodyImageMap.put(avatar, result);
372 * 村に登録されたAvatarのモノクロ顔イメージを返す。
374 * @param avatar Avatar
377 public BufferedImage getAvatarFaceMonoImage(Avatar avatar){
378 BufferedImage result;
379 result = this.faceMonoImageMap.get(avatar);
381 result = getAvatarFaceImage(avatar);
382 result = GUIUtils.createMonoImage(result);
383 this.faceMonoImageMap.put(avatar, result);
389 * 村に登録されたAvatarの全身像イメージを返す。
391 * @param avatar Avatar
394 public BufferedImage getAvatarBodyMonoImage(Avatar avatar){
395 BufferedImage result;
396 result = this.bodyMonoImageMap.get(avatar);
398 result = getAvatarBodyImage(avatar);
399 result = GUIUtils.createMonoImage(result);
400 this.bodyMonoImageMap.put(avatar, result);
410 public BufferedImage getGraveImage(){
411 BufferedImage result = getParentLand().getGraveIconImage();
416 * 国に登録された墓イメージ(大)を返す。
420 public BufferedImage getGraveBodyImage(){
421 BufferedImage result = getParentLand().getGraveBodyImage();
426 * 村にアクセスするためのCGIクエリーを返す。
430 public String getCGIQuery(){
431 StringBuilder result = new StringBuilder();
432 result.append("?vid=").append(getVillageID());
433 return result.toString();
444 public void setLimit(int month, int day, int hour, int minute){
445 this.limitMonth = month;
447 this.limitHour = hour;
448 this.limitMinute = minute;
457 public int getLimitMonth(){
458 return this.limitMonth;
466 public int getLimitDay(){
467 return this.limitDay;
475 public int getLimitHour(){
476 return this.limitHour;
484 public int getLimitMinute(){
485 return this.limitMinute;
491 * @return 無効な村ならfalse
493 public boolean isValid(){
498 * Periodリストの指定したインデックスにPeriodを上書きする。
500 * <p>リストのサイズと同じインデックスを指定する事が許される。
501 * その場合の動作はList.addと同じ。
503 * @param index Periodリストのインデックス。
504 * @param period 上書きするPeriod
505 * @throws java.lang.IndexOutOfBoundsException インデックスの指定がおかしい
507 public void setPeriod(int index, Period period)
508 throws IndexOutOfBoundsException{
509 int listSize = this.periodList.size();
510 if(index == listSize){
511 this.periodList.add(period);
512 }else if(index < listSize){
513 this.periodList.set(index, period);
515 throw new IndexOutOfBoundsException();
521 * アンカーに一致する会話(Talk)のリストを取得する。
526 public List<Talk> getTalkListFromAnchor(Anchor anchor){
527 List<Talk> result = new LinkedList<>();
530 if(anchor.hasTalkNo()){
531 // 事前に全Periodの全会話がロードされているのが前提
532 for(Period period : this.periodList){
533 Talk talk = period.getNumberedTalk(anchor.getTalkNo());
534 if(talk == null) continue;
540 Period anchorPeriod = getPeriod(anchor);
541 if(anchorPeriod == null) return result;
543 // 事前にアンカー対象Periodの全会話がロードされているのが前提
545 for(Topic topic : anchorPeriod.getTopicList()){
546 if( ! (topic instanceof Talk) ) continue;
547 Talk talk = (Talk) topic;
548 if(talk.getHour() != anchor.getHour() ) continue;
549 if(talk.getMinute() != anchor.getMinute()) continue;
556 * 全Periodの発言データをアンロードする。
558 public void unloadPeriods(){
559 for(Period period : this.periodList){
574 public String toString(){
575 return getVillageFullName();