<?php
class User
{
public $name;
public $score;
public function __construct($name, $score)
{
$this->name = $name;
$this->score = $score;
}
public function getInfo()
{
return "{$this->name}, {$this->score}";
}
}
$user1 = new User("Taro", 70);
$user2 = new User("Jiro", 90);
$user1->score = 900;
echo $user1->getInfo() . PHP_EOL;
echo $user2->getInfo() . PHP_EOL;
月: 2024年10月
企画書:「テイルズオブゴッド」〜神を目指すカインの物語〜
企画書:「テイルズオブゴッド」〜神を目指すカインの物語〜
- タイトル
テイルズオブゴッド
〜神を目指すカインの旅〜 - ジャンル
ファンタジー・アクションRPG - ターゲット層
・ファンタジーや神話の物語が好きなプレイヤー
・深いストーリーとキャラクター成長を楽しみたいRPGファン
・過去の「テイルズオブ」シリーズを愛するファン - コンセプト
神に近づくことで、絶対的な力を手にすることができる。人間の枠を超えることを望む者もいれば、神を討ち破ろうとする者もいる。カインは、両親カイルとリアラの影響を受け、神への道を模索しつつ、人間性を維持する葛藤を抱えて成長していく。神に近づく過程での選択や試練を通じ、善悪を超えた力と責任についての理解を深める旅。 - ストーリー概要
プロローグ
魔王との戦いが終わり、平和を取り戻した世界で、カインは平凡に育つ。しかし、ある日、彼の内に秘められた特別な力が覚醒し、「神」に近づくための試練の存在を知ることになる。
「神になること」が自らの運命と信じて疑わないカインは、強さと知恵、そして大きな責任を背負う神を目指すことを決意する。
第一章:選ばれし者の覚醒
カインは神々の力を手に入れるための「試練の地」を目指し、旅立つ。様々な神の眷属や、神を討ち破ろうとする者たちと出会いながら、自己の内面と向き合う旅が始まる。
中盤:仲間との出会いと葛藤
道中で共に神の力を目指す仲間と出会うが、力の使い道や目的で対立することもある。カインは彼らを通じて、力そのものの意味や自分の心の闇と向き合うことになる。
最終章:真実の神との邂逅
試練の最終段階で、カインは「真の神」と呼ばれる存在に出会い、彼が長い間に秘めていた壮大な真実を知る。そして、神になることの重みと、それが人間や世界に及ぼす影響を理解する。
- キャラクター設定
カイン(主人公)
カイルとリアラの息子で、神の力に憧れを抱く青年。心優しく強い意志を持つが、その力が周囲にどう影響するかを学びながら成長していく。
カイル(父親)
元勇者。カインの旅を遠くから見守る。彼自身も神との接触経験があり、カインにその重みを伝えようとする。
リアラ(母親)
癒しの力を持つ存在であり、カインの精神的な支え。彼女は、神を目指すことの危険性を知っているため、息子の旅に不安を抱きつつも信じて見守る。
セリア(仲間)
神に挑む宿命を持つ女性。かつての家族の悲劇をきっかけに、カインと共に力を目指し、次第に深い信頼関係が生まれる。
真の神(最終試練の相手)
世界を司る存在で、神の力を求める者に試練を与える。カインの決意を試し、彼に本当の力と責任を教えようとする。
- ゲームシステム
選択と影響:カインが旅の途中で行う選択が物語の結末に影響を与える。善と悪を超えた選択肢がプレイヤーに提示され、彼の成長がプレイヤー次第で変化。
仲間との絆:仲間の信頼度や絆によって、能力やイベントが変化するシステム。カインの成長に影響を与える。
試練バトル:各試練ごとに、神の眷属や守護者たちとのバトルが発生。バトルでは神の力に関連するスキルやアビリティが解放され、戦略的な選択が求められる。 - ビジュアルイメージ・世界観
幻想的で神秘的な世界:伝説の神殿や、神々の住む高次元の空間、異世界に接する「試練の地」など、神秘的な雰囲気を持つロケーション。
壮大で美麗なキャラクターデザイン:カインの内面の成長と共にビジュアルが変化するようなデザイン。 - 開発スケジュール
フェーズ1:キャラクターやストーリーの設定(3ヶ月)
フェーズ2:バトルと選択システムの実装(6ヶ月)
フェーズ3:アートワークとサウンドデザイン(4ヶ月)
フェーズ4:テストとフィードバックを反映した調整(2ヶ月)
合計開発期間:15ヶ月 - アピールポイント
神に挑む壮大なテーマと、人間としての成長の物語が融合したファンタジーRPG。
プレイヤーの選択によって変化する物語と、エモーショナルなキャラクターの成長が深い感動を与える。
高度な戦略性と美麗なビジュアルが融合したバトルシステム。
カインの葛藤と成長、仲間との絆が紡ぐ「テイルズオブゴッド」は、ファンタジーRPGファンにとって心に残る体験を提供するものです。
PHP getInfoメソッド
<?php
class User
{
public $name;
public $score;
public function __construct($name, $score)
{
$this->name = $name;
$this->score = $score;
}
public function getInfo()
{
return "{$this->name}, {$this->score}";
}
}
$user1 = new User("Taro", 70);
$user2 = new User("Jiro", 90);
echo $user1->getInfo() . PHP_EOL;
echo $user2->getInfo() . PHP_EOL;
PHP プロパティにアクセス
<?php
class User
{
public $name;
public $score;
}
$user1 = new User();
$user1->name = "Taro";
$user1->score = 70;
$user2 = new User();
$user2->name = "Jiro";
$user2->score = 90;
echo $user1->name . PHP_EOL;
echo $user1->score . PHP_EOL;
echo $user2->name . PHP_EOL;
echo $user2->score . PHP_EOL;
新作ゲームのアイディア
1. 時間操作アクションゲーム
- コンセプト: 主人公が時間を操る力を持っており、時間を巻き戻したり、止めたり、早送りしたりして敵を倒したりパズルを解いたりする。
- シナリオ: 主人公は時間を操る特殊能力者で、世界が崩壊しつつある中、過去や未来を行き来しながらその原因を探り、世界を救う。
- 特徴:
- プレイヤーはリアルタイムで時間を操作し、敵の動きを一時停止したり、過去の行動を反映させて戦う。
- パズル要素もあり、時間の概念を使ったステージクリアが鍵。
2. ファンタジー農業シミュレーション+RPG
- コンセプト: ファンタジーの世界で農業をしながら冒険するシミュレーションとRPGの融合。
- シナリオ: プレイヤーは魔法の力を使って農場を経営しつつ、隣接するモンスターの領域を探索する冒険者でもある。土地の力を回復させ、伝説の植物や動物を育てることが目標。
- 特徴:
- 異なる季節や気候に応じて農作物や動物を育て、売買や冒険の装備品を作成。
- 冒険パートでは、農場で育てたアイテムを活用し、モンスターと戦いながら新しいエリアを解放。
3. ロボットカスタマイズ対戦アクション
- コンセプト: 自分でロボットをカスタマイズして対戦するオンラインアクションゲーム。
- シナリオ: 未来の世界で、プレイヤーはカスタマイズ可能なバトルロボットのパイロットとなり、さまざまなアリーナで他のプレイヤーと戦う。
- 特徴:
- パーツの組み合わせで異なる性能や武器を持つロボットを作成。
- 対戦モードや協力プレイモードを搭載し、友達と協力して巨大なボスに挑むモードも。
4. 異世界転生シュミレーションRPG
- コンセプト: プレイヤーが異世界に転生し、新たな運命を切り開くシミュレーションRPG。
- シナリオ: 異世界に転生した主人公が、戦略的な選択をしながら国を築いたり、ギルドを統率したり、仲間を集めて世界の運命を左右する。
- 特徴:
- 戦闘だけでなく、仲間との絆を深めたり、外交や内政を行う要素も充実。
- ターン制バトルと、拠点経営のバランスが取れたゲーム進行。
5. ソーシャルAIと連携した未来型RPG
- コンセプト: プレイヤーの選択や行動がAIで生成されるストーリーやクエストに影響を与えるRPG。
- シナリオ: 近未来の世界でAIが人々の生活に密接に関わっており、プレイヤーが様々な選択肢を通じてAIとの共存を探る。
- 特徴:
- プレイヤーの行動に基づき、AIがリアルタイムで新しいクエストやストーリー展開を生成。
- AIの発展やプレイヤーとのインタラクションによってエンディングが変わるマルチエンディング方式。
どのアイディアも、ユーザーの興味に応じてカスタマイズできる要素があります。気になる方向性や詳細を教えてもらえれば、さらに深掘りした提案もできますよ!
4o
あなた:
もっと
ChatGPT:
ChatGPT
さらにいくつかの新作ゲームのアイディアを提案します。ユニークな体験を提供できる要素を意識してみました。
6. 迷宮生成ローグライクRPG
- コンセプト: 毎回ランダム生成される迷宮を攻略し、深部にいるボスを倒すローグライクRPG。
- シナリオ: 古代の迷宮を探索する冒険者となり、迷宮内で集めたアイテムや能力を駆使して、変化し続けるダンジョンを攻略。
- 特徴:
- 毎回異なるマップや敵配置で、新鮮な挑戦を提供。
- 迷宮からの脱出を目指す他に、特定のミッション(救出やアイテム収集)をこなすサブクエストも充実。
7. 夢世界探検ゲーム
- コンセプト: プレイヤーが夢の中の不思議な世界を探検するアドベンチャーゲーム。
- シナリオ: 主人公が毎晩見る夢の中で、現実と夢が交錯する世界を旅し、謎を解き明かしていく。夢世界には奇妙で魅力的なキャラクターが多数登場し、現実世界に影響を与える出来事が起こる。
- 特徴:
- 独特のアートスタイルやデザインで夢の世界を表現。
- 夢の中で起こる出来事が、現実世界にも影響を与えるユニークな双方向システム。
8. 巨大都市の経済シミュレーション
- コンセプト: プレイヤーが巨大都市の経済を管理し、発展させるシミュレーションゲーム。
- シナリオ: プレイヤーは市長として、都市の成長や市民の幸福を目指してインフラ整備や産業発展に取り組む。技術の発展や自然災害などのランダムイベントに対応しながら、理想の都市を築く。
- 特徴:
- リアルタイムで変動する経済や社会の要素を管理。
- プレイヤーの選択によって異なる都市が形成され、結果的に平和的な都市や技術先進都市など多様な都市像が生まれる。
9. 進化型モンスター育成アクション
- コンセプト: 自分の育てたモンスターが戦闘で進化し、独自の能力を得て成長するアクションゲーム。
- シナリオ: プレイヤーは未知のモンスターを発見し、それを育てて進化させる。そのモンスターと共に世界を冒険し、強力な敵や他のモンスターと戦う。
- 特徴:
- モンスターの進化はプレイヤーの戦い方や選択に応じて分岐。
- モンスター同士の合体やスキルのカスタマイズが可能で、無限の成長パターンを実現。
10. 環境破壊がテーマのサバイバルRPG
- コンセプト: 破壊された自然環境を修復しながら生き延びるサバイバルゲーム。
- シナリオ: 未来の世界で環境破壊が進んだ地球を舞台に、プレイヤーは自然を回復させつつ生存していく。生態系を守り、植物や動物を保護しながら、資源を集めて自分の拠点を強化する。
- 特徴:
- 環境保護とサバイバルがテーマで、プレイヤーの選択が世界の再生に直接影響。
- プレイヤーの行動によって、エコシステムが回復し、動植物が蘇る演出。
11. 音楽バトルRPG
- コンセプト: 音楽を武器にして戦うリズムアクションRPG。
- シナリオ: 音楽の力が世界を支配している未来都市で、プレイヤーは楽器を使って敵とリズムに乗って戦う。楽曲のリズムに合わせて攻撃や防御を行い、敵の音波攻撃を跳ね返す。
- 特徴:
- リズムに基づいたバトルシステムで、音楽とアクションが一体化。
- プレイヤーが自由に作曲し、バトルに使用できる楽曲エディター機能。
12. マルチプレイヤー探偵ゲーム
- コンセプト: プレイヤー全員が探偵役となり、事件の真相を解明する協力型オンラインゲーム。
- シナリオ: 一つの都市で連続殺人事件が発生し、プレイヤーたちは各自異なる手がかりを手にして、犯人を突き止めるため協力し合う。情報交換や推理を重ねて解決に導く。
- 特徴:
- 各プレイヤーに異なる情報が提供され、それを元に推理を進める協力型ゲーム。
- リアルタイムで変化するストーリーや証拠が、毎回異なる結末を生む。
13. 多次元空間を探索する冒険ゲーム
- コンセプト: 異なる次元に存在する無限の世界を探索しながら、失われた技術を取り戻すSFアドベンチャー。
- シナリオ: プレイヤーは次元を行き来する能力を持った主人公として、崩壊寸前の世界を救うため、各次元に散らばる技術を集める。
- 特徴:
- 各次元ごとに異なる物理法則やルールがあり、次元ごとの攻略方法が異なる。
- サバイバル、パズル、アクション要素がバランスよく融合。
これらのアイディアは、ジャンルやテーマごとに異なる特徴を持たせることで、プレイヤーに多様な体験を提供することを意識しています。興味があるアイディアについて、さらに詳細を考えたい場合は、どうぞお知らせください!
勇者の聖典.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>勇者の聖典 - クエスト&バトル</title>
<style>
body {
font-family: 'Verdana', sans-serif;
background-color: #121212;
color: white;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
header {
text-align: center;
padding: 20px;
background-color: rgba(0, 0, 0, 0.9);
width: 100%;
position: fixed;
top: 0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6);
}
h1 {
font-size: 50px;
margin: 0;
text-shadow: 2px 2px 10px #ffcc00;
}
.container {
background-color: rgba(0, 0, 0, 0.9);
padding: 30px;
border-radius: 15px;
text-align: center;
width: 90%;
max-width: 600px;
margin-top: 100px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6);
}
.button {
padding: 15px 30px;
font-size: 18px;
margin: 10px;
cursor: pointer;
background-color: #ffcc00;
border: none;
border-radius: 5px;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: #ffaa00;
}
.hidden {
display: none;
}
footer {
background-color: rgba(0, 0, 0, 0.9);
padding: 20px;
width: 100%;
text-align: center;
position: absolute;
bottom: 0;
}
select {
font-size: 18px;
padding: 10px;
margin: 10px;
background-color: #444;
color: white;
border: none;
border-radius: 5px;
}
</style>
</head>
<body>
<header>
<h1>勇者の聖典 - クエスト&バトル</h1>
</header>
<!-- メニュー画面 -->
<div class="container" id="menu-container">
<h2>メニュー</h2>
<button class="button" onclick="showTraining()">訓練場</button>
<button class="button" onclick="showQuests()">クエストへ</button>
<button class="button" onclick="showEquipment()">装備</button>
<button class="button" onclick="showEnhancement()">装備の強化</button>
</div>
<!-- 訓練場画面 -->
<div class="container hidden" id="training-container">
<h2>訓練を行ってレベルアップしましょう!</h2>
<div class="training-actions">
<button class="button" onclick="train('軽い訓練', 50)">軽い訓練 (50 XP)</button>
<button class="button" onclick="train('中程度の訓練', 100)">中程度の訓練 (100 XP)</button>
<button class="button" onclick="train('厳しい訓練', 200)">厳しい訓練 (200 XP)</button>
</div>
<button class="button" onclick="returnToMenu()">メニューに戻る</button>
</div>
<!-- クエスト選択画面 -->
<div class="container hidden" id="quest-container">
<h2>クエストを選んでください</h2>
<div class="quests">
<button class="button" onclick="startQuest('王国への召喚', 'なし', 0, 0, 50, 'なし')">
王国への召喚 (50 XP)
</button>
<button class="button" onclick="startQuest('初めての試練', 'ゴブリン', 500, 50, 70, '光の剣')">
初めての試練 (70 XP)
</button>
<button class="button" onclick="startQuest('燃え盛る山の危機', '火山の魔物', 1800, 250, 200, '炎の剣')">
燃え盛る山の危機 (200 XP)
</button>
<button class="button" onclick="startQuest('氷の洞窟の秘宝', '氷の魔物', 2000, 220, 250, 'アイススピア')">
氷の洞窟の秘宝 (250 XP)
</button>
</div>
<button class="button" onclick="returnToMenu()">メニューに戻る</button>
</div>
<!-- 装備画面 -->
<div class="container hidden" id="equipment-container">
<h2>装備を選んでください</h2>
<select id="equipment-select">
<option value="光の剣">光の剣 (攻撃力 +20)</option>
<option value="ドラゴンの鱗の盾">ドラゴンの鱗の盾 (防御力 +30)</option>
<option value="精霊の指輪">精霊の指輪 (防御力 +10, 攻撃力 +10)</option>
<option value="炎の剣">炎の剣 (攻撃力 +40)</option>
<option value="アイススピア">アイススピア (攻撃力 +25, 防御力 +10)</option>
<option value="暗黒の大剣">暗黒の大剣 (攻撃力 +50, 防御力 -10)</option>
</select>
<button class="button" onclick="equipItem()">装備する</button>
<button class="button" onclick="returnToMenu()">メニューに戻る</button>
</div>
<!-- 装備の強化画面 -->
<div class="container hidden" id="enhancement-container">
<h2>装備の強化</h2>
<select id="enhance-select">
<option value="光の剣">光の剣 (攻撃力 +20)</option>
<option value="ドラゴンの鱗の盾">ドラゴンの鱗の盾 (防御力 +30)</option>
<option value="精霊の指輪">精霊の指輪 (防御力 +10, 攻撃力 +10)</option>
<option value="炎の剣">炎の剣 (攻撃力 +40)</option>
<option value="アイススピア">アイススピア (攻撃力 +25, 防御力 +10)</option>
<option value="暗黒の大剣">暗黒の大剣 (攻撃力 +50, 防御力 -10)</option>
</select>
<button class="button" onclick="enhanceItem()">強化する</button>
<button class="button" onclick="returnToMenu()">メニューに戻る</button>
</div>
<!-- 戦闘画面 -->
<div class="container hidden" id="battle-container">
<h2 id="battle-title">敵と戦っています...</h2>
<div class="status">
<div id="player-status">
<h3>勇者のステータス</h3>
<p>HP: <span id="player-hp">1000</span></p>
<p>攻撃力: <span id="player-attack">100</span></p>
<p>防御力: <span id="player-defense">50</span></p>
<p>レベル: <span id="player-level">1</span></p>
<p>経験値: <span id="player-xp">0</span> / <span id="xp-to-next-level">100</span></p>
</div>
<div id="enemy-status">
<h3>敵のステータス</h3>
<p>名前: <span id="enemy-name">ドラゴン</span></p>
<p>HP: <span id="enemy-hp">1000</span></p>
<p>攻撃力: <span id="enemy-attack">150</span></p>
</div>
</div>
<div class="actions">
<button class="button" onclick="attack()">攻撃</button>
<button class="button" onclick="useSkill()">スキル使用</button>
<button class="button" onclick="useItem()">アイテム使用</button>
</div>
</div>
<!-- 報酬画面 -->
<div class="container hidden" id="reward-container">
<h2>クエスト完了!</h2>
<p id="reward-message">報酬を受け取って次のクエストへ進みましょう。</p>
<p id="item-reward">獲得したアイテム: <span id="reward-item"></span></p>
<button class="button" onclick="startNextQuest()">次のクエストへ</button>
<button class="button" onclick="returnToMenu()">メニューに戻る</button>
</div>
<script>
let player = {
class: '',
hp: 1000,
maxHp: 1000,
attack: 100,
defense: 50,
level: 1,
xp: 0,
xpToNextLevel: 100,
skills: [],
inventory: {
healingPotion: 2,
items: []
},
equipped: {
weapon: '',
shield: ''
}
};
let enemy = {
name: '',
hp: 0,
attack: 0
};
let currentQuestReward = '';
function train(trainingType, xpEarned) {
alert(`${trainingType}を行い、${xpEarned} XPを獲得しました!`);
player.xp += xpEarned;
checkLevelUp();
}
function checkLevelUp() {
while (player.xp >= player.xpToNextLevel) {
player.level++;
player.xp -= player.xpToNextLevel;
player.xpToNextLevel += 50; // 次のレベルアップに必要なXPを増加
player.maxHp += 100; // レベルアップでHPが上昇
player.attack += 10; // レベルアップで攻撃力が上昇
player.defense += 5; // レベルアップで防御力が上昇
alert(`レベルアップ!現在のレベルは ${player.level} です!`);
}
updateStatus();
}
function updateStatus() {
document.getElementById('player-hp').textContent = player.hp;
document.getElementById('player-attack').textContent = player.attack;
document.getElementById('player-defense').textContent = player.defense;
document.getElementById('player-level').textContent = player.level;
document.getElementById('player-xp').textContent = player.xp;
document.getElementById('xp-to-next-level').textContent = player.xpToNextLevel;
}
function showTraining() {
document.getElementById('menu-container').classList.add('hidden');
document.getElementById('training-container').classList.remove('hidden');
}
function returnToMenu() {
document.getElementById('training-container').classList.add('hidden');
document.getElementById('quest-container').classList.add('hidden');
document.getElementById('battle-container').classList.add('hidden');
document.getElementById('reward-container').classList.add('hidden');
document.getElementById('equipment-container').classList.add('hidden');
document.getElementById('enhancement-container').classList.add('hidden');
document.getElementById('menu-container').classList.remove('hidden');
}
function showQuests() {
document.getElementById('menu-container').classList.add('hidden');
document.getElementById('quest-container').classList.remove('hidden');
}
function showEquipment() {
document.getElementById('menu-container').classList.add('hidden');
document.getElementById('equipment-container').classList.remove('hidden');
}
function showEnhancement() {
document.getElementById('menu-container').classList.add('hidden');
document.getElementById('enhancement-container').classList.remove('hidden');
}
function startQuest(questName, enemyName, enemyHp, enemyAttack, xpReward, rewardItem) {
enemy.name = enemyName;
enemy.hp = enemyHp;
enemy.attack = enemyAttack;
currentQuestReward = rewardItem; // クエスト報酬アイテム
player.xp += xpReward; // クエスト完了時にXPを加算
document.getElementById('battle-title').textContent = `${enemyName}との戦い`;
document.getElementById('enemy-name').textContent = enemy.name;
document.getElementById('enemy-hp').textContent = enemy.hp;
document.getElementById('enemy-attack').textContent = enemy.attack;
document.getElementById('quest-container').classList.add('hidden');
document.getElementById('battle-container').classList.remove('hidden');
updateStatus();
}
function equipItem() {
const selectedItem = document.getElementById('equipment-select').value;
if (selectedItem === '光の剣') {
player.attack += 20;
player.equipped.weapon = '光の剣';
} else if (selectedItem === 'ドラゴンの鱗の盾') {
player.defense += 30;
player.equipped.shield = 'ドラゴンの鱗の盾';
} else if (selectedItem === '精霊の指輪') {
player.attack += 10;
player.defense += 10;
player.equipped.weapon = '精霊の指輪';
} else if (selectedItem === '炎の剣') {
player.attack += 40;
player.equipped.weapon = '炎の剣';
} else if (selectedItem === 'アイススピア') {
player.attack += 25;
player.defense += 10;
player.equipped.weapon = 'アイススピア';
} else if (selectedItem === '暗黒の大剣') {
player.attack += 50;
player.defense -= 10;
player.equipped.weapon = '暗黒の大剣';
}
alert(`${selectedItem}を装備しました!`);
updateStatus();
}
function enhanceItem() {
const selectedItem = document.getElementById('enhance-select').value;
if (selectedItem === '光の剣') {
player.attack += 10; // 強化による攻撃力アップ
alert('光の剣を強化して攻撃力 +10!');
} else if (selectedItem === 'ドラゴンの鱗の盾') {
player.defense += 10; // 強化による防御力アップ
alert('ドラゴンの鱗の盾を強化して防御力 +10!');
} else if (selectedItem === '精霊の指輪') {
player.attack += 5;
player.defense += 5;
alert('精霊の指輪を強化して攻撃力 +5、防御力 +5!');
} else if (selectedItem === '炎の剣') {
player.attack += 15;
alert('炎の剣を強化して攻撃力 +15!');
} else if (selectedItem === 'アイススピア') {
player.attack += 10;
player.defense += 5;
alert('アイススピアを強化して攻撃力 +10、防御力 +5!');
} else if (selectedItem === '暗黒の大剣') {
player.attack += 20;
player.defense += 5; // 強化による防御力の補正
alert('暗黒の大剣を強化して攻撃力 +20、防御力 +5!');
}
updateStatus();
}
function attack() {
const damage = Math.max(0, player.attack - enemy.attack / 4);
enemy.hp -= damage;
document.getElementById('enemy-hp').textContent = Math.max(0, enemy.hp);
if (enemy.hp <= 0) {
victory();
} else {
enemyAttack();
}
}
function enemyAttack() {
const damage = Math.max(0, enemy.attack - player.defense / 4);
player.hp -= damage;
document.getElementById('player-hp').textContent = Math.max(0, player.hp);
if (player.hp <= 0) {
defeat();
}
}
function useSkill() {
if (player.skills.length > 0) {
const skillDamage = 150; // スキルのダメージ
enemy.hp -= skillDamage;
alert(`スキルを使用して${skillDamage}ダメージを与えました!`);
document.getElementById('enemy-hp').textContent = Math.max(0, enemy.hp);
if (enemy.hp <= 0) {
victory();
} else {
enemyAttack();
}
} else {
alert("スキルをまだ習得していません!");
}
}
function useItem() {
if (player.inventory.healingPotion > 0) {
player.hp = Math.min(player.maxHp, player.hp + 200);
player.inventory.healingPotion--;
alert("回復薬を使用してHPを200回復しました!");
updateStatus();
} else {
alert("回復薬がありません!");
}
}
function victory() {
alert("勝利しました!");
document.getElementById('battle-container').classList.add('hidden');
document.getElementById('reward-container').classList.remove('hidden');
document.getElementById('reward-item').textContent = currentQuestReward;
player.inventory.items.push(currentQuestReward); // Add reward to player's items
checkLevelUp();
}
function defeat() {
alert("敗北しました!");
document.getElementById('battle-container').classList.add('hidden');
document.getElementById('quest-container').classList.remove('hidden');
}
function startNextQuest() {
document.getElementById('reward-container').classList.add('hidden');
document.getElementById('quest-container').classList.remove('hidden');
}
window.onload = function() {
document.getElementById('training-container').classList.add('hidden');
document.getElementById('quest-container').classList.add('hidden');
document.getElementById('battle-container').classList.add('hidden');
document.getElementById('reward-container').classList.add('hidden');
document.getElementById('equipment-container').classList.add('hidden');
document.getElementById('enhancement-container').classList.add('hidden');
document.getElementById('menu-container').classList.remove('hidden');
updateStatus();
};
</script>
</body>
</html>
NekoNekodouga.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ねこねこ動画 - 動画共有サイト</title>
<style>
/* ベーススタイル */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
header {
background-color: #333;
color: white;
padding: 10px;
text-align: center;
position: fixed;
width: 100%;
top: 0;
z-index: 100;
}
header h1 {
margin: 0;
font-size: 24px;
}
nav ul {
list-style-type: none;
padding: 0;
margin: 10px 0 0 0;
text-align: center;
}
nav ul li {
display: inline-block;
margin-right: 15px;
}
nav ul li a {
color: white;
text-decoration: none;
font-size: 16px;
}
#searchBar {
margin-top: 10px;
padding: 5px;
width: 300px;
max-width: 80%;
border: none;
border-radius: 4px;
}
.container {
margin-top: 100px;
padding: 20px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
.upload-container, .video-info, .description, .comments, .related-videos, .gallery {
background-color: white;
padding: 20px;
margin-top: 20px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.upload-container h2, .video-info h2, .description h2, .comments h2, .related-videos h2, .gallery h2 {
margin-top: 0;
}
.upload-container form input[type="file"],
.upload-container form input[type="text"],
.upload-container form textarea,
.upload-container form select {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
border: 1px solid #ccc;
}
.upload-container form button {
padding: 10px 20px;
background-color: #333;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.upload-container form button:hover {
background-color: #555;
}
.video-container {
position: relative;
text-align: center;
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.video-container video {
width: 100%;
height: auto;
border-radius: 10px;
}
.comment-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.comment {
position: absolute;
white-space: nowrap;
font-size: 20px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
animation: moveComment linear forwards;
}
@keyframes moveComment {
from {
left: 100%;
}
to {
left: -100%;
}
}
.description p, .comments p, .gallery p {
margin: 10px 0;
}
.comments form textarea {
width: 100%;
padding: 10px;
border-radius: 4px;
border: 1px solid #ccc;
resize: vertical;
margin-bottom: 10px;
}
.comments form select {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
}
.comments form button {
padding: 10px 20px;
background-color: #333;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.comments form button:hover {
background-color: #555;
}
.comment-list .comment {
background-color: #f9f9f9;
padding: 10px;
margin-bottom: 10px;
border-radius: 5px;
}
.like-button, .dislike-button {
background-color: #333;
color: white;
border: none;
padding: 5px 10px;
margin: 5px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.like-button:hover, .dislike-button:hover {
background-color: #555;
}
footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
footer p {
margin: 0;
}
@media (max-width: 768px) {
nav ul li {
display: block;
margin: 10px 0;
}
#searchBar {
width: 80%;
}
.video-container {
max-width: 100%;
}
}
</style>
</head>
<body>
<!-- ヘッダー開始 -->
<header>
<h1>ねこねこ動画</h1>
<nav>
<ul>
<li><a href="#">ホーム</a></li>
<li><a href="#">人気動画</a></li>
<li><a href="#">カテゴリ</a></li>
<li><a href="#">ログイン</a></li>
</ul>
</nav>
<input type="text" id="searchBar" placeholder="動画を検索..." />
</header>
<!-- ヘッダー終了 -->
<div class="container">
<!-- 動画アップロードエリア -->
<div class="upload-container">
<h2>動画をアップロード</h2>
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" id="videoFile" name="video" accept="video/*" required><br>
<input type="text" id="videoTitle" name="title" placeholder="動画タイトル" required><br>
<textarea id="videoDescription" name="description" rows="4" placeholder="動画の説明" required></textarea><br>
<button type="submit">アップロード</button>
</form>
<div id="uploadStatus"></div>
</div>
<!-- プレビューエリア -->
<div class="video-container" id="previewContainer" style="display: none;">
<h2 id="previewTitle">プレビュー: 動画タイトル</h2>
<video controls id="videoPreview"></video>
<div class="comment-overlay" id="commentOverlay"></div>
<p id="previewDescription">プレビュー: 動画の説明</p>
</div>
<!-- コメント投稿エリア -->
<div class="comments">
<h2>コメントを投稿</h2>
<form id="commentForm">
<textarea id="commentText" rows="4" cols="50" placeholder="コメントを追加..." required></textarea><br>
<label for="commentTime">表示タイミング (秒):</label>
<input type="number" id="commentTime" placeholder="0" min="0" required><br>
<label for="commentColor">コメントの色:</label>
<select id="commentColor">
<option value="white">白</option>
<option value="red">赤</option>
<option value="blue">青</option>
<option value="green">緑</option>
<option value="yellow">黄色</option>
</select><br>
<label for="commentFontSize">フォントサイズ:</label>
<select id="commentFontSize">
<option value="20px">小</option>
<option value="24px">中</option>
<option value="28px">大</option>
</select><br>
<label for="commentSpeed">表示速度 (秒):</label>
<input type="number" id="commentSpeed" placeholder="10" min="5" max="20" required><br>
<button type="submit">投稿</button>
</form>
<div id="commentList" class="comment-list"></div>
</div>
<!-- 評価機能 -->
<div class="rating">
<button class="like-button" id="likeButton">👍 いいね</button>
<span id="likeCount">0</span>
<button class="dislike-button" id="dislikeButton">👎 バッド</button>
<span id="dislikeCount">0</span>
</div>
</div>
<!-- フッター開始 -->
<footer>
<p>© 2023 ねこねこ動画. All Rights Reserved.</p>
</footer>
<!-- フッター終了 -->
<script>
let comments = [];
// 動画のアップロードとプレビュー処理
document.getElementById("uploadForm").addEventListener("submit", function(event) {
event.preventDefault();
const fileInput = document.getElementById("videoFile");
const file = fileInput.files[0];
const title = document.getElementById("videoTitle").value;
const description = document.getElementById("videoDescription").value;
if (!file) {
alert("動画ファイルを選択してください。");
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const videoData = e.target.result;
document.getElementById("previewContainer").style.display = "block";
document.getElementById("videoPreview").src = videoData;
document.getElementById("previewTitle").textContent = "プレビュー: " + title;
document.getElementById("previewDescription").textContent = "プレビュー: " + description;
};
reader.readAsDataURL(file);
});
// コメント投稿処理
document.getElementById("commentForm").addEventListener("submit", function(event) {
event.preventDefault();
const commentText = document.getElementById("commentText").value;
const commentTime = parseInt(document.getElementById("commentTime").value);
const commentColor = document.getElementById("commentColor").value;
const commentFontSize = document.getElementById("commentFontSize").value;
const commentSpeed = parseInt(document.getElementById("commentSpeed").value);
if (commentText && commentTime >= 0) {
const comment = {
text: commentText,
time: commentTime,
color: commentColor,
fontSize: commentFontSize,
speed: commentSpeed
};
comments.push(comment);
displayCommentInList(comment);
document.getElementById("commentForm").reset();
}
});
// コメントリストに表示
function displayCommentInList(comment) {
const commentList = document.getElementById("commentList");
const commentDiv = document.createElement("div");
commentDiv.classList.add("comment");
commentDiv.textContent = `タイミング: ${comment.time}秒 - ${comment.text}`;
commentList.appendChild(commentDiv);
}
// 動画の再生に合わせてコメントを表示
const videoElement = document.getElementById("videoPreview");
videoElement.addEventListener("timeupdate", function() {
const currentTime = Math.floor(videoElement.currentTime);
comments.forEach(comment => {
if (comment.time === currentTime) {
displayCommentOnVideo(comment);
}
});
});
// 動画上にコメントを流す
function displayCommentOnVideo(comment) {
const overlay = document.getElementById("commentOverlay");
const commentElement = document.createElement("div");
commentElement.classList.add("comment");
commentElement.textContent = comment.text;
commentElement.style.color = comment.color;
commentElement.style.fontSize = comment.fontSize;
commentElement.style.animationDuration = `${comment.speed}s`;
commentElement.style.top = Math.random() * 80 + "%"; // ランダムな位置
overlay.appendChild(commentElement);
// 一定時間後にコメントを削除
setTimeout(() => {
commentElement.remove();
}, comment.speed * 1000);
}
// いいね・バッド機能
document.getElementById("likeButton").addEventListener("click", function() {
let likeCount = parseInt(document.getElementById("likeCount").textContent);
likeCount++;
document.getElementById("likeCount").textContent = likeCount;
});
document.getElementById("dislikeButton").addEventListener("click", function() {
let dislikeCount = parseInt(document.getElementById("dislikeCount").textContent);
dislikeCount++;
document.getElementById("dislikeCount").textContent = dislikeCount;
});
</script>
</body>
</html>
ELDER.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ELDER - ソーシャルネットワーキングサービス</title>
<!-- CSSファイルの読み込み -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
<!-- RiTa.jsライブラリの読み込み -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/rita/1.3.63/rita-full.min.js"></script>
<style>
/* 既存のスタイルシート */
body {
padding-top: 20px;
background: linear-gradient(to right, #f8f9fa, #e9ecef);
font-family: 'Arial', sans-serif;
color: #343a40;
}
.navbar {
background-color: #007bff;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.navbar-brand,
.nav-link {
color: #fff !important;
font-weight: bold;
}
.navbar-toggler {
border: none;
}
.card {
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
.card-body {
padding: 30px;
}
.form-control {
border-radius: 20px;
border: 1px solid #ced4da;
padding: 15px;
transition: border-color 0.3s;
}
.form-control:focus {
border-color: #007bff;
box-shadow: none;
}
.btn-primary {
background-color: #007bff;
border: none;
border-radius: 20px;
padding: 10px 20px;
font-weight: bold;
transition: background-color 0.3s, transform 0.3s;
}
.btn-primary:hover {
background-color: #0056b3;
transform: translateY(-3px);
}
.btn-secondary {
border-radius: 20px;
padding: 10px 20px;
font-weight: bold;
transition: background-color 0.3s, transform 0.3s;
}
.btn-secondary:hover {
background-color: #6c757d;
transform: translateY(-3px);
}
.profile-img {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.text-center h1 {
font-weight: 700;
font-size: 2.5em;
margin-bottom: 20px;
color: #007bff;
}
.comment-section {
margin-top: 20px;
padding: 20px;
background-color: #f1f3f5;
border-radius: 8px;
}
.comment-section .card {
border: none;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.modal-content {
border-radius: 15px;
overflow: hidden;
}
.modal-header {
background-color: #007bff;
color: white;
border-bottom: none;
}
.modal-footer {
border-top: none;
}
.form-error {
color: red;
font-size: 0.9em;
}
/* タイムラインのデザイン */
#timeline {
margin-top: 20px;
}
#timeline .card {
border-radius: 15px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
overflow: hidden;
background-color: #fff;
}
#timeline .card-body {
padding: 20px;
}
#timeline .btn {
border-radius: 20px;
}
/* 投稿のシェアボタン */
.btn-share {
background-color: #ff9800;
border: none;
border-radius: 20px;
padding: 5px 10px;
color: #fff;
font-weight: bold;
transition: background-color 0.3s;
}
.btn-share:hover {
background-color: #e68900;
}
/* X(旧Twitter)シェアボタン */
.btn-share-twitter {
background-color: #1da1f2;
border: none;
border-radius: 20px;
padding: 5px 10px;
color: #fff;
font-weight: bold;
transition: background-color 0.3s;
}
.btn-share-twitter:hover {
background-color: #1480bf;
}
/* 通知エリア */
#notificationArea {
position: fixed;
top: 80px;
right: 20px;
z-index: 1000;
max-width: 300px;
}
/* FEEDアイコンをRSSの文字に変更 */
.nav-item .rss-icon {
font-weight: bold;
color: #fff;
}
</style>
</head>
<body>
<!-- ナビゲーションバー -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">ELDER</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="ナビゲーションの切り替え">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="#" onclick="showLoginForm()">ログイン</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="showRegisterForm()">登録</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="showProfile()" style="display: none;" id="navProfile">プロフィール</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="logout()" style="display: none;" id="navLogout">ログアウト</a>
</li>
<!-- FEEDアイコンをRSSの文字に変更 -->
<li class="nav-item" id="navFeed" style="display: none;">
<a class="nav-link rss-icon" href="#" onclick="showFeedForm()">RSS</a>
</li>
</ul>
</div>
</nav>
<div class="container">
<!-- ログインフォーム -->
<div id="loginForm" class="card w-50 mx-auto">
<div class="card-body">
<h2 class="card-title text-center mb-4">ログイン</h2>
<div class="form-group">
<input type="text" id="loginUsername" class="form-control" placeholder="ユーザー名">
<span id="loginUsernameError" class="form-error" style="display: none;">ユーザー名を入力してください</span>
</div>
<div class="form-group">
<input type="password" id="loginPassword" class="form-control" placeholder="パスワード">
<span id="loginPasswordError" class="form-error" style="display: none;">パスワードを入力してください</span>
</div>
<button onclick="login()" class="btn btn-primary btn-block">ログイン</button>
<button onclick="showRegisterForm()" class="btn btn-secondary btn-block">登録</button>
</div>
</div>
<!-- 登録フォーム -->
<div id="registerForm" class="card w-50 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">登録</h2>
<div class="form-group">
<input type="text" id="registerName" class="form-control" placeholder="氏名">
<span id="registerNameError" class="form-error" style="display: none;">氏名を入力してください</span>
</div>
<div class="form-group">
<input type="text" id="registerUsername" class="form-control" placeholder="ユーザー名">
<span id="registerUsernameError" class="form-error" style="display: none;">ユーザー名を入力してください</span>
</div>
<div class="form-group">
<input type="password" id="registerPassword" class="form-control" placeholder="パスワード">
<span id="registerPasswordError" class="form-error" style="display: none;">パスワードを入力してください</span>
</div>
<div class="form-group">
<input type="password" id="registerConfirmPassword" class="form-control" placeholder="パスワードを確認">
<span id="registerConfirmPasswordError" class="form-error" style="display: none;">パスワードが一致しません</span>
</div>
<button onclick="register()" class="btn btn-primary btn-block">登録</button>
<button onclick="showLoginForm()" class="btn btn-secondary btn-block">ログインに戻る</button>
</div>
</div>
<!-- プロフィール -->
<div id="profile" class="card w-50 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">プロフィール</h2>
<div class="text-center mb-3">
<img id="profileImg" class="profile-img" src="default_profile_img.jpg" alt="プロフィール画像">
</div>
<p class="text-center mb-2"><strong>氏名:</strong> <span id="profileName"></span></p>
<p class="text-center mb-4"><strong>ユーザー名:</strong> <span id="profileUsername"></span></p>
<div class="form-group">
<input type="file" id="profileImageInput" accept="image/*" class="form-control-file">
</div>
<button onclick="uploadProfileImage()" class="btn btn-primary btn-block"><i class="fas fa-upload"></i> プロフィール画像をアップロード</button>
<button onclick="logout()" class="btn btn-danger btn-block"><i class="fas fa-sign-out-alt"></i> ログアウト</button>
</div>
</div>
<!-- 投稿フォーム -->
<div id="postForm" class="card w-75 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">投稿を作成</h2>
<div class="form-group">
<textarea id="postContent" class="form-control" rows="3" placeholder="今何を考えていますか?"></textarea>
</div>
<button onclick="createPost()" class="btn btn-primary btn-block">投稿</button>
</div>
</div>
<!-- FEED登録フォーム -->
<div id="feedForm" class="card w-75 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">FEEDを登録</h2>
<div class="form-group">
<input type="text" id="feedUrl" class="form-control" placeholder="RSSフィードのURL">
<span id="feedUrlError" class="form-error" style="display: none;">RSSフィードのURLを入力してください</span>
</div>
<button onclick="registerFeed()" class="btn btn-primary btn-block">登録</button>
<div class="form-group mt-4">
<label for="feedInterval">自動投稿の間隔(秒):</label>
<input type="number" id="feedInterval" class="form-control" placeholder="例:60" min="1">
</div>
<button onclick="setFeedInterval()" class="btn btn-secondary btn-block">間隔を設定</button>
</div>
</div>
<!-- AIBOT投稿登録フォーム -->
<div id="aiBotForm" class="card w-75 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">BOT投稿を登録</h2>
<div class="form-group">
<textarea id="aiBotContent" class="form-control" rows="3" placeholder="BOTに投稿させたい内容を入力してください"></textarea>
</div>
<button onclick="registerAiBotPost()" class="btn btn-primary btn-block">BOT投稿を登録</button>
</div>
</div>
<!-- タイムライン -->
<div id="timeline" class="w-75 mx-auto mt-4"></div>
<!-- 通知エリア -->
<div id="notificationArea" class="alert alert-info" style="display: none;"></div>
</div>
<!-- リプライモーダル -->
<div class="modal fade" id="replyModal" tabindex="-1" role="dialog" aria-labelledby="replyModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<!-- モーダルの内容は後ほどJavaScriptで制御します -->
<div class="modal-header">
<h5 class="modal-title" id="replyModalLabel">投稿に返信</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="閉じる">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<textarea id="replyContent" class="form-control" rows="3" placeholder="返信をここに書いてください..."></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">閉じる</button>
<button type="button" onclick="postReply()" class="btn btn-primary">返信</button>
</div>
</div>
</div>
</div>
<!-- スクリプトの読み込み -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<!-- BootstrapのJavaScript -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<!-- RiTa.jsライブラリの読み込み(既にhead内で読み込み済み) -->
<script>
// ユーザー、投稿、フィード、チャット関連のデータ
let currentUser = null;
let users = [];
let posts = [];
let feeds = [];
let notifications = [];
let aiBotPosts = [];
let feedFetchInterval = 60000; // デフォルトは60,000ミリ秒(60秒)
let feedFetchTimer;
let markovBotInterval = 60000; // マルコフ連鎖BOTの投稿間隔
// ローカルストレージの読み込み・保存関連の関数
function saveUsersToLocalStorage() {
localStorage.setItem('users', JSON.stringify(users));
}
function savePostsToLocalStorage() {
localStorage.setItem('posts', JSON.stringify(posts));
}
function saveFeedsToLocalStorage() {
localStorage.setItem('feeds', JSON.stringify(feeds));
}
function saveAiBotPostsToLocalStorage() {
localStorage.setItem('aiBotPosts', JSON.stringify(aiBotPosts));
}
function saveCurrentUserToLocalStorage() {
if (currentUser) {
localStorage.setItem('currentUser', JSON.stringify(currentUser));
} else {
localStorage.removeItem('currentUser');
}
}
function loadUsersFromLocalStorage() {
const storedUsers = localStorage.getItem('users');
if (storedUsers) {
users = JSON.parse(storedUsers);
}
}
function loadPostsFromLocalStorage() {
const storedPosts = localStorage.getItem('posts');
if (storedPosts) {
posts = JSON.parse(storedPosts);
}
}
function loadFeedsFromLocalStorage() {
const storedFeeds = localStorage.getItem('feeds');
if (storedFeeds) {
feeds = JSON.parse(storedFeeds);
}
}
function loadAiBotPostsFromLocalStorage() {
const storedAiBotPosts = localStorage.getItem('aiBotPosts');
if (storedAiBotPosts) {
aiBotPosts = JSON.parse(storedAiBotPosts);
}
}
function loadCurrentUserFromLocalStorage() {
const storedCurrentUser = localStorage.getItem('currentUser');
if (storedCurrentUser) {
currentUser = JSON.parse(storedCurrentUser);
}
}
// フォームの表示・非表示
function hideAll() {
document.getElementById('loginForm').style.display = 'none';
document.getElementById('registerForm').style.display = 'none';
document.getElementById('profile').style.display = 'none';
document.getElementById('postForm').style.display = 'none';
document.getElementById('feedForm').style.display = 'none';
document.getElementById('aiBotForm').style.display = 'none';
}
function showLoginForm() {
hideAll();
document.getElementById('loginForm').style.display = 'block';
document.getElementById('navProfile').style.display = 'none';
document.getElementById('navLogout').style.display = 'none';
document.getElementById('navFeed').style.display = 'none';
}
function showRegisterForm() {
hideAll();
document.getElementById('registerForm').style.display = 'block';
}
function showProfile() {
hideAll();
document.getElementById('profile').style.display = 'block';
document.getElementById('profileName').textContent = currentUser.name;
document.getElementById('profileUsername').textContent = currentUser.username;
document.getElementById('postForm').style.display = 'block';
document.getElementById('feedForm').style.display = 'block';
document.getElementById('aiBotForm').style.display = 'block';
loadProfileImage(); // プロフィール画像をロード
displayTimeline(); // タイムラインを表示
document.getElementById('navProfile').style.display = 'block';
document.getElementById('navLogout').style.display = 'block';
document.getElementById('navFeed').style.display = 'block'; // RSSナビゲーションを表示
}
function showFeedForm() {
hideAll();
document.getElementById('feedForm').style.display = 'block';
}
// ユーザー認証関連の関数
function validateLoginForm() {
let valid = true;
const username = document.getElementById('loginUsername').value;
const password = document.getElementById('loginPassword').value;
if (!username) {
document.getElementById('loginUsernameError').style.display = 'block';
valid = false;
} else {
document.getElementById('loginUsernameError').style.display = 'none';
}
if (!password) {
document.getElementById('loginPasswordError').style.display = 'block';
valid = false;
} else {
document.getElementById('loginPasswordError').style.display = 'none';
}
return valid;
}
function login() {
if (!validateLoginForm()) {
return;
}
const username = document.getElementById('loginUsername').value;
const password = document.getElementById('loginPassword').value;
const user = users.find(u => u.username === username && u.password === password);
if (user) {
currentUser = user;
saveCurrentUserToLocalStorage(); // currentUserを保存
showProfile();
} else {
alert('ユーザー名またはパスワードが間違っています。');
}
}
function logout() {
currentUser = null;
saveCurrentUserToLocalStorage(); // currentUserを削除
hideAll();
showLoginForm();
}
function validateRegisterForm() {
let valid = true;
const name = document.getElementById('registerName').value;
const username = document.getElementById('registerUsername').value;
const password = document.getElementById('registerPassword').value;
const confirmPassword = document.getElementById('registerConfirmPassword').value;
if (!name) {
document.getElementById('registerNameError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerNameError').style.display = 'none';
}
if (!username) {
document.getElementById('registerUsernameError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerUsernameError').style.display = 'none';
}
// ユーザー名の重複チェック
if (users.find(u => u.username === username)) {
document.getElementById('registerUsernameError').textContent = 'このユーザー名は既に使用されています';
document.getElementById('registerUsernameError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerUsernameError').style.display = 'none';
}
if (!password) {
document.getElementById('registerPasswordError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerPasswordError').style.display = 'none';
}
if (password !== confirmPassword) {
document.getElementById('registerConfirmPasswordError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerConfirmPasswordError').style.display = 'none';
}
return valid;
}
function register() {
if (!validateRegisterForm()) {
return;
}
const name = document.getElementById('registerName').value;
const username = document.getElementById('registerUsername').value;
const password = document.getElementById('registerPassword').value;
users.push({ name, username, password, profileImage: null });
saveUsersToLocalStorage(); // ユーザー情報を保存
alert('登録が完了しました!ログインしてください。');
showLoginForm();
}
// プロフィール画像の処理
function loadProfileImage() {
const profileImg = document.getElementById('profileImg');
// プロフィール画像が設定されている場合はその画像を表示
if (currentUser.profileImage) {
profileImg.src = currentUser.profileImage;
} else {
// デフォルトのプロフィール画像を表示
profileImg.src = 'default_profile_img.jpg';
}
}
function uploadProfileImage() {
const input = document.getElementById('profileImageInput');
const file = input.files[0];
if (file) {
// FileReaderを使用して画像を読み込む
const reader = new FileReader();
reader.onload = function(e) {
// 読み込んだ画像をプロフィール画像として設定
currentUser.profileImage = e.target.result;
// プロフィール画像を更新して表示
loadProfileImage();
saveUsersToLocalStorage(); // プロフィール画像を保存
saveCurrentUserToLocalStorage(); // currentUserを更新
}
reader.readAsDataURL(file);
}
}
// AIBOT投稿を登録
function registerAiBotPost() {
const content = document.getElementById('aiBotContent').value;
if (content.trim() !== '') {
aiBotPosts.push(content);
saveAiBotPostsToLocalStorage();
alert("BOTの投稿が登録されました!");
document.getElementById('aiBotContent').value = ''; // 入力欄を空にする
}
}
// AIBOTが定期的に投稿
function postAiBotContent() {
if (aiBotPosts.length > 0) {
const content = aiBotPosts[Math.floor(Math.random() * aiBotPosts.length)];
const aiUser = {
name: 'BOT',
username: 'bot',
profileImage: 'default_ai_profile_img.jpg'
};
const post = {
id: Date.now(),
content: content,
author: aiUser.name,
authorUsername: aiUser.username,
authorProfileImage: aiUser.profileImage,
likes: 0,
comments: []
};
posts.unshift(post);
savePostsToLocalStorage();
displayTimeline();
}
}
setInterval(postAiBotContent, 500000); // 50,000ミリ秒 = 50秒
// マルコフ連鎖を使用した自動投稿BOT
function generateMarkovChainText(order = 2) {
const allTexts = posts.map(post => post.content).join(' ');
if (allTexts.length < 100) return null; // テキストが短すぎる場合は生成しない
const markov = new RiTa.Markov(order);
markov.addText(allTexts);
const sentences = markov.generate(1);
return sentences[0];
}
function postMarkovBotContent() {
const generatedText = generateMarkovChainText(3); // 階数を3に設定
if (generatedText) {
const botUser = {
name: 'MarkovBot',
username: 'markovbot',
profileImage: 'markov_bot_profile_img.jpg'
};
const post = {
id: Date.now(),
content: generatedText,
author: botUser.name,
authorUsername: botUser.username,
authorProfileImage: botUser.profileImage,
likes: 0,
comments: []
};
posts.unshift(post);
savePostsToLocalStorage();
displayTimeline();
}
}
setInterval(postMarkovBotContent, markovBotInterval); // マルコフ連鎖BOTの投稿間隔
// 投稿関連の関数
function createPost() {
const postContent = document.getElementById('postContent').value;
if (postContent.trim() !== '') {
const post = {
id: Date.now(),
content: postContent,
author: currentUser.name,
authorUsername: currentUser.username,
authorProfileImage: currentUser.profileImage,
likes: 0,
comments: [],
shares: 0
};
posts.unshift(post); // 最新の投稿を先頭に追加
savePostsToLocalStorage(); // 投稿を保存
displayTimeline();
document.getElementById('postContent').value = ''; // 投稿後、入力欄を空にする
}
}
function displayTimeline() {
const timeline = document.getElementById('timeline');
timeline.innerHTML = '';
posts.forEach(post => {
const postElement = document.createElement('div');
postElement.classList.add('card', 'mb-3');
postElement.innerHTML = `
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<img src="${post.authorProfileImage || 'default_profile_img.jpg'}" class="profile-img mr-2" alt="プロフィール画像">
<div>
<strong>${post.author}</strong> (@${post.authorUsername})
${post.shared ? '<span class="badge badge-info ml-2">シェア</span>' : ''}
</div>
</div>
<p class="card-text">${post.content}</p>
${post.link ? `<a href="${post.link}" target="_blank" class="btn btn-link">続きを読む</a>` : ''}
<div class="d-flex justify-content-between">
<div>
<button onclick="likePost(${post.id})" class="btn btn-primary btn-sm"><i class="fas fa-thumbs-up"></i> いいね (${post.likes})</button>
<button onclick="toggleComments(${post.id})" class="btn btn-secondary btn-sm"><i class="fas fa-comments"></i> コメント (${post.comments.length})</button>
<button onclick="replyToPost(${post.id})" class="btn btn-info btn-sm"><i class="fas fa-reply"></i> 返信</button>
<button onclick="sharePost(${post.id})" class="btn btn-share btn-sm"><i class="fas fa-share"></i> シェア (${post.shares || 0})</button>
<button onclick="shareToTwitter(${post.id})" class="btn btn-share-twitter btn-sm"><i class="fab fa-twitter"></i> Xでシェア</button>
</div>
<small class="text-muted">${new Date(post.id).toLocaleString()}</small>
</div>
<div id="comments-${post.id}" class="comment-section mt-3" style="display: none;">
${post.comments.map(comment => `
<div class="card mb-2">
<div class="card-body">
<p><strong>${comment.author}</strong>: ${comment.content}</p>
</div>
</div>
`).join('')}
</div>
</div>
`;
timeline.appendChild(postElement);
});
}
// いいね機能
function likePost(postId) {
const post = posts.find(p => p.id === postId);
post.likes++;
savePostsToLocalStorage();
displayTimeline();
}
// コメント表示切替
function toggleComments(postId) {
const commentSection = document.getElementById(`comments-${postId}`);
commentSection.style.display = commentSection.style.display === 'none' ? 'block' : 'none';
}
// リプライ機能
function replyToPost(postId) {
const modal = document.getElementById('replyModal');
modal.dataset.postId = postId;
$('#replyModal').modal('show');
}
function postReply() {
const postId = parseInt(document.getElementById('replyModal').dataset.postId);
const post = posts.find(p => p.id === postId);
const replyContent = document.getElementById('replyContent').value;
if (replyContent.trim() !== '') {
post.comments.push({ author: currentUser.name, content: replyContent });
$('#replyModal').modal('hide');
savePostsToLocalStorage();
displayTimeline();
} else {
alert('返信を入力してください。');
}
}
// 投稿のシェア機能
function sharePost(postId) {
const post = posts.find(p => p.id === postId);
if (post) {
const sharedPost = {
...post,
id: Date.now(),
author: currentUser.name,
authorUsername: currentUser.username,
authorProfileImage: currentUser.profileImage,
shared: true,
likes: 0,
comments: [],
shares: post.shares ? post.shares + 1 : 1
};
posts.unshift(sharedPost);
savePostsToLocalStorage();
displayTimeline();
alert('投稿をシェアしました。');
}
}
// X(旧Twitter)にシェアする機能
function shareToTwitter(postId) {
const post = posts.find(p => p.id === postId);
if (post) {
let shareText = post.content;
if (post.link) {
shareText += ' ' + post.link;
}
const shareUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${encodeURIComponent(window.location.href)}`;
window.open(shareUrl, '_blank');
}
}
// FEEDの登録と自動投稿機能
function registerFeed() {
const feedUrl = document.getElementById('feedUrl').value;
if (feedUrl.trim() !== '') {
feeds.push(feedUrl);
saveFeedsToLocalStorage(); // フィード情報を保存
alert('RSSフィードが登録されました!');
document.getElementById('feedUrl').value = ''; // 登録後、入力欄を空にする
} else {
document.getElementById('feedUrlError').style.display = 'block';
}
}
function fetchFeeds() {
feeds.forEach(feedUrl => {
fetch(`https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(feedUrl)}`)
.then(response => response.json())
.then(data => {
data.items.forEach(item => {
// 同じ投稿が既に存在するかチェック
if (!posts.some(p => p.link === item.link)) {
const post = {
id: Date.now() + Math.random(), // 重複しないIDを生成
content: item.title,
link: item.link, // リンクを追加
author: 'FEEDBOT',
authorUsername: 'feedbot',
authorProfileImage: 'feedbot_profile_img.jpg', // FEEDBOTのプロフィール画像を設定
likes: 0,
comments: []
};
posts.unshift(post); // 最新の投稿を先頭に追加
savePostsToLocalStorage(); // 投稿を保存
}
});
displayTimeline(); // タイムラインを表示
})
.catch(error => console.error('RSSフィードの取得エラー:', error));
});
}
function setFeedInterval() {
const intervalInput = document.getElementById('feedInterval').value;
if (intervalInput && parseInt(intervalInput) > 0) {
feedFetchInterval = parseInt(intervalInput) * 1000; // ミリ秒に変換
clearInterval(feedFetchTimer);
feedFetchTimer = setInterval(fetchFeeds, feedFetchInterval);
alert(`自動投稿の間隔が${intervalInput}秒に設定されました。`);
} else {
alert('有効な間隔を入力してください(1以上の数字)。');
}
}
// ページロード時にフィードの自動取得を開始
function startFeedFetch() {
feedFetchTimer = setInterval(fetchFeeds, feedFetchInterval);
}
// 初期化
function init() {
loadUsersFromLocalStorage();
loadPostsFromLocalStorage();
loadFeedsFromLocalStorage();
loadAiBotPostsFromLocalStorage();
loadCurrentUserFromLocalStorage();
if (currentUser) {
showProfile();
} else {
hideAll();
document.getElementById('loginForm').style.display = 'block';
}
displayTimeline();
startFeedFetch(); // フィードの自動取得を開始
}
// ページロード時に初期化
window.onload = init;
</script>
</body>
</html>
PHP array_map
<?php
// $addFive = function($n)
// {
// return $n + 5;
// };
// $addFive = fn($n) => $n + 5;
$scores = [70, 90, 80];
// $updatedScores = array_map($addFive, $scores);
$updatedScores = array_map(fn($n) => $n + 5, $scores);
print_r($updatedScores);
新作アニメのアイディア
タイトル: 「無限の境界(Infinity Edge)」
ジャンル: SF、ファンタジー、サスペンス
あらすじ:
未来の地球、人類は高度な技術と魔法が融合した世界に生きている。しかし、この世界は「無限の境界」という謎の空間によって二つの異なる次元に分断されていた。一方の次元では人々が技術と魔法を調和させて平和に暮らし、もう一方の次元では技術は廃れ、魔法が暴走し、混乱と戦争が続いている。
主人公は、次元を渡る力を持つ少女「レイナ」。彼女はある日、両次元をつなぐ「無限の境界」を越える使命を与えられ、平和な次元から混乱する次元へと旅立つ。しかし、彼女が境界を越えるたびに、次元のバランスが崩れ、現実そのものが歪んでいくことに気づく。
レイナは、各次元に存在する「守護者」と呼ばれる異能の者たちと協力しながら、両次元を救うための方法を探し出さなければならない。だが、彼女が渡り歩く先々で出会う者たちには、それぞれ異なる真実があり、次元の崩壊には隠された陰謀が絡んでいる。
主なキャラクター:
- レイナ: 主人公。次元を渡る力を持つが、その力は彼女自身も完全には制御できない。優しく聡明だが、次第に重い決断を迫られる。
- エイジ: 混乱する次元の守護者で、冷静かつ戦略的なリーダータイプ。かつては平和な次元の人間だったが、ある事件でこの次元に送り込まれた。
- ミラ: 平和な次元の科学者で、技術と魔法の融合を研究している。レイナを支援し、次元崩壊の原因を探る。
- カイン: 次元崩壊を引き起こしている影の組織のリーダー。両次元を破壊して新しい世界を創ろうとしている野心家。
特徴:
- 次元移動: 異なる次元に行くたびに、風景やルール、物理法則が変化する。魔法が支配する世界、テクノロジーが主流の未来都市、荒廃した戦場など、様々な舞台設定が可能。
- 深い世界観: 技術と魔法が共存する未来、異なる次元の歴史や文化が詳細に描かれ、視聴者を没入させる。
- サスペンス要素: 両次元の背後に隠された秘密や、登場人物たちの裏切り、陰謀が物語を盛り上げる。
- ダークファンタジー要素: レイナは次元を救うために善悪の境界に悩むことになり、苦しい選択を迫られる。
このアイディアは、壮大なスケールで次元や時間、陰謀が絡み合うSFファンタジーとして展開することができ、視覚的にもドラマティックな演出が期待できると思います